Fully Responsive Theme
Resize your Browser to see the Effect
Parallax Effect
Scroll and Notice the Header Image

Dynamic Rules engine – update

As an exercise in working with C#, I decided it was time I translated my dynamic rules engine over from VB.

Here is the translated Github gist: C# Dynamic Rules Engine

A rules engine itself is a useful concept for many scenarios, business rules, validation, filtering and more.

At the time I wrote this, I was working with legacy code, no ORM for the database – and classes generated from underlying database tables. It was a class per table, nothing more complex than this.

An operation was performed – by a third party – to merge 2 databases into 1. However, we couldn’t rely on the primary key that was in use any more. A proportion of records had their keys replaced. Not an ideal scenario but it was what we had.

A method of fact checking our table rows was required as various transformations were applied, in code, to try and line up the relationships to child tables in the database. Please note, there were no foreign key constraints either.

As a novice programmer (when does one come out of that title?), I figured that I could help – a little – if there was a way to show a row from a table in database 1 was the same as a row in the matching table in database 3 (the merged database). The only solution I saw at the time was fact checking columns. This required human intervention and knowledge of the data, which I had. About 12 critical tables were involved.

The aim of the game was to create a definition of the members (representing columns) that must match both sides for any row to be a match for it’s twin in the other database’s table. For example: a name, an address and an NI was enough to say this record for a person is the same on both sides. But a name and address was less certain.

As an exercise, I elected to work on this at home and learn what I could to find the neatest way to do this dodgy sounding task.

In the process I covered generics, interfaces, reflection and expression trees. That was a Friday evening – I was done by 2am.

The code worked, we were able to test it out on a number of cases and found that 99% of the data was fine, the rest was easily bought into line.

There’s a long list of reasons why such fact checking, in the way I describe here, is not a good idea. However, as an exercise in learning the programming concepts covered, I still am pleased with the original code. An F# version is likely to be a touch shorter – especially if I attempt it functionally!

Last caveat: The human aspect of having to know the columns means this is unviable for most scenarios and I regard the approach as a piece of throwaway code. Also, it could be argued as too complex a solution for the task at hand. Especially with the performance cost of the amount of reflection used.

The snippet below is the heart of the code:

/// <summary>
    /// The Dynamic Rules Engine itself
    /// Here we have to pass in a class instance and the properties to be matched.
    /// using reflection to get the members, put them in a list and generate a list of lambda predicates expressions
    /// We compile and run these - they all have to be true to get a match. We return a match item, if one exists.
    /// </summary>

    private T getMatchingMerge(T item, List<PropertyInfo> matchProperties)
        {
        List<PropertyInfo> getPropList = item.GetType().GetProperties().Where(s =>
                       s.Equals(matchProperties.Where(x => x.Name == s.Name).FirstOrDefault())).ToList();
        if (propDict.Count < matchProperties.Count)
            {
              propDict = getPropList.ToDictionary(p => matchProperties.Where(s => s.Name == p.Name).First());
            }

        if (_rules.Count != matchProperties.Count) {
            _rules.Clear();  
            foreach (var lambda in from Pair in propDict
                                   let leftProperty = Pair.Key
                                   let rightProperty = Pair.Value
                                   let leftParameter = Expression.Parameter(rightProperty.DeclaringType, rightProperty.DeclaringType.Name)
                                   let rightParameter = Expression.Parameter(rightProperty.DeclaringType, rightProperty.DeclaringType.Name)
                                   let left = Expression.Property(leftParameter, leftProperty)
                                   let right = Expression.Property(rightParameter, rightProperty)
                                   let leftEqualsRight = Expression.Equal(left, right)
                                   // select Expression.Lambda<Func<T, T, bool>>(leftEqualsRight, Expression), new Parameter.Expression() { leftParameter, rightParameter }).Compile()
                                   select Expression.Lambda<Func<T, T, bool>>((Expression)leftEqualsRight, new ParameterExpression[] { leftParameter, rightParameter }).Compile())
                _rules.Add(lambda);
        }
        T _match = MergeList.Where(r => _rules.All(x => x.Invoke(item, r) == true)).FirstOrDefault();
        return _match; //I've not covered the path where there is no match!
     }

C# Dynamic Rules Engine