InfiniTec - Henning Krauses Blog

Don't adjust your mind - it's reality that is malfunctioning

Fun with .NET 4 expressions

The other day I was tasked to write some code to synchronize data between two data stores. The entities were similar but not exactly the same. And I didn’t just want to synchronize all entries every time, but only if the source entity was different than the target entity. To simplify the comparison of each property, I wrote a small function which compared the values of one property of the source entity with the matching property on the target entity and updated it if it was different.

Let’s assume we have a simple entity class like this one:

class Contact
{
    public string Name { get; set; }
    public int Age { get; set; }
}

The method I write allows me to write this code to update a target entry with just a few lines of code:

var source = new Contact {Name = "Alice", Age = 42};
var target = new Contact();

if (UpdatePropertyIfChanged(source, target, contact => contact.Name, contact => contact.Name) | 
    UpdatePropertyIfChanged(source, target, contact => contact.Age, contact => contact.Age))
{
    Console.Out.WriteLine("Contact updated.");
    Console.Out.WriteLine("target.Name = {0}", target.Name);
    Console.Out.WriteLine("target.Age = {0}", target.Age);
}

The important method is the UpdatePropertyIfChanged method. It takes two entities, the source and the target instance. The third parameter is a lambda method which selects the property on the source instance and the last parameter selects the destination property on the target instance. To ensure that every property is checked, I used a logical or instead of a conditional or to ensure that every property is checked. If I had used the conditional or operator instead and the first property was different on both objects, the second property would not have been checked and updated.

Here is the body of the UpdatePropertyIfChanged method:

private static bool UpdatePropertyIfChanged<TSource, TProperty, TTarget>(TSource source, TTarget target, Func<TSource, TProperty> sourceSelector, Expression<Func<TTarget, TProperty>> targetSelector)
{
    var valueA = sourceSelector(source);
    var valueB = targetSelector.Compile()(target);

    if (EqualityComparer<TProperty>.Default.Equals(valueA, valueB)) return false;

    var setterMethod = CreateSetter(targetSelector);
    setterMethod(target, valueA);
    return true;
}

It’s pretty straigtforward, except for one details: Instead of a Func<TSource, TProperty> like the sourceSelector, the targetSelector is of type Expression<Func<TTarget, TProperty>>. The main difference is that an expression of type Func is already compiled and ready to be executed. In contrast, an expression of type Expression<Func> can be examined and manipulated at runtime.

The method executes these steps:

  1. Read the value of the property from the source entity by execute the sourceSelector expression
  2. Read the value of the property from the target entity by compiling and executing the targetSelector expression
  3. Compare the two values using the EqualityComparer<T>.Default comparer.
  4. If the two values are equal, just return false and abort the method.
  5. Convert the expression from the targetSelector expression to a setter expression
  6. Call the newly created expression with the value from the sourceSelector expression.

The ability to compile your own lambda expression has been introduced with .NET 3.5. But they were limited to reading properties and field. This feature has been improved with .NET 4.

Here is the body of the CreateSetter method:

private static Action<TInstance, TProperty> CreateSetter<TInstance, TProperty>(Expression<Func<TInstance, TProperty>> getterMethod)
{
    var memberExpression = getterMethod.Body as MemberExpression;

    if (memberExpression == null) throw new InvalidOperationException("GetterMethod must be a memberexpression");

    var instance = Expression.Parameter(typeof(TInstance));
    var parameter = Expression.Parameter(typeof(TProperty));
    var expression =
        Expression.Lambda<Action<TInstance, TProperty>>(
            Expression.Assign(
                Expression.MakeMemberAccess(instance, memberExpression.Member), parameter), new[] { instance, parameter });
    return expression.Compile();
}
This method expects an expression which maps an instance to a property of that instance. Based on this expression, a new expression is created, which sets that property to a specified value. The method returns this new expression in the form of an Action<TInstance, TProperty>.

Tags: ,

Technorati: ,

Posted by Henning Krause on Wednesday, November 24, 2010 4:52 PM, last modified on Tuesday, July 26, 2011 9:57 PM
Permalink | Post RSSRSS comment feed

Comments (4) -

On 11/25/2010 10:23:36 AM Magesh India wrote:

Magesh

Thanks for the great article.  When i tried to compile your code, i get &amp;amp;quot;No overload for method &amp;#39;Parameter&amp;#39; takes 1 arguments&amp;amp;quot; in the &amp;amp;quot;Expression.Parameter(typeof(TInstance))&amp;amp;quot; statements and the code is not compiling.  Any Idea?  

On 11/25/2010 10:25:47 AM hkrause Germany wrote:

hkrause

Hi,
thanks. Are you working with .NET 4.0? This Framework version is required for the code to work.

Henning

On 11/25/2010 1:07:32 PM JeroenH Belgium wrote:

JeroenH

neat, but looks like a pretty darn expensive way to simply synchronize data...

On 11/26/2010 6:38:14 PM Aaron United States wrote:

Aaron

I concur, have you performed any performance diagnostics against this code?  What are the ramifications of synchronizing a list of objects, say 100 objects with 10 properties?

Cool article, nontheless..

 +Pingbacks and trackbacks (2)