The Problem

Entity Framework, and other ORM tools like it, make our jobs easier.  The cost of building data classes, and maintaining them over time, becomes much lower as these tools take over the mapping and code generation steps for us.  It gives us a certain freedom from the data layer, but at a cost.  We have repository and unit of work patterns to consistently control data access and stored queries, but there isn’t a whole lot of consistency on how to encode the business logic in an equally transparent way.  Additionally, adding methods to objects can become cumbersome to manage, while also requiring future developers to know how to use them correctly.

Wouldn’t it be nice if we could:

using(some context) {
    find an object
    change it accordingly based on the needed functionality
    save the changes in the context
}

With the confidence that all applicable business rules were applied?  I believe one of the best ways to ensure business logic is applied 100% of the time is to make sure it can’t be ignored or overridden without specifically doing so.  The only way to achieve that goal is to make the business logic invisible.

A Solution

One way to ensure consistent enforcement is change the .SaveChanges() method in the DbContext created by the framework.  This is a method that is called consistently, and therefore makes sense as a point to add in a mechanism for business rule enforcement.  Writing your EF context into a UnitOfWork type pattern can accomplish this.  Additionally, the DbContext is nice enough to give us a list of changed objects within the .ChangeTracker property.

Now that we know where to insert the rules and have a means to determine what the rules need to operate on, we need to determine how to organize the rules and make sure they are processed consistently.  Enter the rule engine:

Rule Flowchart

Essentially, it does what the chart says: for each object given, find all rules for that type of object and execute them on the object.  Repeat for every object that’s been changed.

Sounds great; how do we get it done?

Start with an interface for the rules to follow:

public interface IBusinessRule<T>
{
    bool Enforce(T input);
}

Simple enough; an Enforce method that enforces whatever the rule needs to do.  The interface is generic, allowing us to specify the class it belongs to.  Time to build the engine around it.  A basic implementation will require two methods: one to add rules and one to enforce them upon a particular object.

Our rule engine will need to know what rules to run for each class of objects, so we start with that:

private Dictionary<Type, List<Type>> Rules;

It will then need a means of adding rules to the engine.  If you’ll notice our dictionary is storing type information, so our add method will need to be created accordingly:

public void AddRule<T>()
{
    if (Rules == null)
    {
        Rules = new Dictionary<Type, List<Type>>();
    }
    var typeinfo = typeof(T);
    var interfaces = typeinfo.GetInterfaces();
    var generic = interfaces.FirstOrDefault(nx => nx.GetGenericTypeDefinition() == typeof(IBusinessRule<>));
    if(generic != null) {
        var ruletarget = generic.GetGenericArguments()[0];
        if (!Rules.ContainsKey(ruletarget))
        {
            Rules.Add(ruletarget, new List<Type>());
        }
    Rules[ruletarget].Add(typeinfo);
}

This method essentially performs the following:

  • verifies that Rules is a valid Dictionary
  • gets the information of the type specified
  • searches that information for implementations of IBusinessRule<>
  • for each implementation found, add it to Rules accordingly

Now that we have a defined list of rules and a means of adding to them, we need to define how to apply these rules to an object of the correct class:

public bool Process(object input)
{
    var targetType = input.GetType();
    var success = true;
    if (Rules.ContainsKey(targetType))
    {
        foreach (var rule in Rules[targetType])
        {
            var ro = Activator.CreateInstance(rule);
            var rt = ro.GetType();
            var rm = rt.GetMethod("Enforce");
            var parms = new object[] { input };
            success = success && (bool)rm.Invoke(ro, parms);
            if (rt.GetInterfaces().Contains(typeof(IDisposable)))
            {
                var rmd = rt.GetMethod("Dispose");
                rmd.Invoke(ro, null);
            }
        }
    }
    return (success);
}

This method gets the job done by performing the following:

  • get the type of the object passed
  • find the rules for this type
  • using reflection, instantiate an object of each rule class
  • call .Enforce on the instantiated object, passing in the object to be processed
  • because we’re using reflection, ensure any disposable objects are disposed of accordingly

Wrap those methods in a RuleEngine class, and you’ve got the hard part done. Now it’s just a matter of looping it into the Entity Framework flow. This can be accomplished by creating a derived class from the DbContext class automatically generated by the framework. In the derived class, the SaveChanges() method can be overridden, giving us the space needed to enforce the rules.

public class BusinessContext : YourDbContextClass {
    public override int SaveChanges()
    {
        // assumptions: a RuleEngine exists and has been given rules via .AddRule()
        foreach(var obj in this.ChangeTracker.Entries())
        {
            RuleEngine.Process(obj);
        }
        return(base.SaveChanges());
    }
}

That’s it! Now you’re free to change objects as needed, secure with the knowledge that rules will be applied accordingly.