Getting rid of null checks in property chains with - but not limited to - AutoMapper and S#arpArchitecture

by sandord 5. August 2010 16:08
kick it on DotNetKicks.com Shout it

As I was doing some entity to DTO mapping using AutoMapper, I found myself writing a lot of null checks when mapping nested properties. Those checks are often necessary because one or more properties in the chain may be null.

For example,

  string zipCode = customer.Address.ZipCode;

may throw a NullReferenceException when either customer or Address is null.

This is often handled by writing code like this:

  string zipCode = (customer != null ? (customer.Address != null ?
    (customer.Address.ZipCode) : null) : null);

or even

  string zipCode = null;

  if (customer != null && customer.Address != null)
  {
      zipCode = customer.Address.ZipCode;
  }

As you can see, both solutions are a quite verbose and distracting of their purpose while all we want to is get to the property our customer’s zip code is stored in. You can imagine that this problem gets worse when you have to write such code for say 50 properties in a row.

So the goal is staying as close to the code in the first example while providing a protection against NullReferenceException and return null (actually the default value of the final property’s type) if any property in the chain appears to be null.

Based on some reading I did on the new features concerning expression trees in .NET 4.0, I figured they would come to good use for solving this problem.

The basic idea was to write a method that:

  • takes our expression as a lambda expression
  • builds an expression tree made up from the fragments of our expression, embellished with null checks where appropriate
  • either returns the value of the final property or the default value of the type of the final property

Thanks to the addition of the BlockExpression to .NET 4.0, we can now easily build the expression tree we need. Prior to .NET 4.0, we had to resort to building multiple expression trees, generate IL or use reflection, options that are either overly complex (thus hard to read) or performing badly.

Now, I realize that this idea is not unique – as goes for most ideas – but the implementations I have seen are not very elegant or performing very well so we’ll add those two conditions as well.

So, here goes. Advocating proper Test Driven Design, I’ll show a few tests first. I’m not going to be exhaustive, that would clutter up this post too much but I’ll provide all code for download so don’t worry.

public void SafeGet_UsingNestedProperty_ReturnsPropertyResult()
{
    // Arrange.
    string stringValue = "Hello";

    TestEntity subject = new TestEntity()
    {
        Property1 = new TestEntityPropertyType()
        {
            Property3 = stringValue
        }
    };

    // Act.
    object result = NullSafeExpressionHandler.SafeGet(subject,
        n => n.Property1.Property3.Length);

    // Assert.
    Assert.IsNotNull(result);
    Assert.IsTrue(result is int);
    Assert.AreEqual(stringValue.Length, (int)result);
}

public void SafeGet_UsingNestedReferenceTypePropertyWithNullInChain_ReturnsNull()
{
    // Arrange.
    TestEntity subject = new TestEntity();

    // Act.
    object result = NullSafeExpressionHandler.SafeGet(subject,
        n => n.Property1.Property3);

    // Assert.
    Assert.AreEqual(result, null);
}


public void SafeGet_UsingNestedValueTypePropertyWithNullInChain_ReturnsDefault()
{
    // Arrange.
    TestEntity subject = new TestEntity();

    // Act.
    object result = NullSafeExpressionHandler.SafeGet(subject,
        n => n.Property1.Property3.Length);

    // Assert.
    Assert.IsNotNull(result);
    Assert.AreEqual(result.GetType(), typeof(int));
    Assert.AreEqual(result, default(int));
}

That should give a solid impression of what we’re getting. That is, being able to do the following:

string zipCode = NullSafeExpressionHandler.SafeGet(customer, n => n.Address.ZipCode);

But... that’s still not as brief as we’d like it to be. Writing an extension method should improve on that quite a bit. TDD purists: I’m sorry but I’m not going to show a unit test for that this time.

public static TResult SafeGet<TSource, TResult>(this TSource instance,
    Expression<Func<TSource, TResult>> expression)
    where TSource : object
{
    if (instance == null)
    {
        throw new ArgumentNullException("instance");
    }
    else if (expression == null)
    {
        throw new ArgumentNullException("expression");
    }

    return NullSafeExpressionHandler.SafeGet(instance, expression);
}

As you can see, I used a generic source type instead of just extending object. The reason behind that is, that I think that extending object is not a good idea in general. When doing so, you’re introducing your extension method to almost every type in scope. Imagine what would happen if lots of folks started doing that and how your intellisense dropdown will look like.

What I propose instead, is creating extension methods for specific base classes that are closely related to the problem domain. I mentioned AutoMapper (I’m fond of the project so I’ll keep turning it into a link whenever I type it) already and I’m going to show an extension method for S#arpArchitecture as well. Heck, I’ll even start with it.

S#arpArchitecture

Here’s the extension method that provides null-safe expression evaluation for S#arpArchitecture entities. S#arpArchitecture (I have to be fair, I love that project too) is is currently very NHibernate (yes, lovely) flavored and is often used in conjunction with AutoMapper.

public static TResult NullSafeGet<TSource, TResult>(this TSource instance,
    Expression<Func<TSource, TResult>> expression)
    where TSource : SharpArch.Core.DomainModel.Entity
{
    if (instance == null)
    {
        throw new ArgumentNullException("instance");
    }
    else if (expression == null)
    {
        throw new ArgumentNullException("expression");
    }

    return NullSafeExpressionHandler.SafeGet(instance, expression);
}

See? That’s was really easy, I just changed the generic constraint on the source type to use S#arpArchitecture’s Entity class.

AutoMapper

For AutoMapper things are a little bit more complicated but still not much. The context is a bit different because when we are defining mappings, we provide lambda expressions already. Here’s an example:

CreateMap<Order, OrderDto>()
    .ForMember(n => n.CustomerZipCode, p => p.MapFrom(q => q.Customer != null ?
        (q.Customer.Address != null ? (q.Customer.Address.ZipCode) : null) : null));

That's quite a bulky expression! Luckily, the extension method allows us to write:

CreateMap<Order, OrderDto>()
    .ForMember(n => n.CustomerZipCode, p =>
        p.NullSafeMapFrom(q => q.Customer.Address.ZipCode));

Here’s the extension method:

public static void NullSafeMapFrom<TSource, TMember>(this IMemberConfigurationExpression<TSource> instance,
    Expression<Func<TSource, TMember>> mapping)
{
    if (instance == null)
    {
        throw new ArgumentNullException("instance");
    }
    else if (mapping == null)
    {
        throw new ArgumentNullException("mapping");
    }

    instance.MapFrom(NullSafeExpressionHandler.GetSafeGetDelegate<TSource, TMember>(mapping));
}

Basically, we're just hijacking the lambda expression, quickly adding our little magic to it.

Implementation

Earlier on I was talking about expression trees, elegancy and performance. What I like about expression tree so much is that writing code to build them is actually quite readable, in contrast to reflection and certainly when compared to code that generates IL explicitly.

As we can compile expression trees to delegates, they can perform very well. Of course, there is a need for a caching mechanism because compiling an expression is expensive.

I'm not going to show all code here, that would be a little too much but I'll provide a link to the source code below. The code that I will show here is the core of the solution, the code that builds the expression tree.

private static Func<TSource, TResult> CreateSafeGetDelegate<TSource, TResult>(IList<MemberExpression> expressionFragments)
{
    List<Expression> targetFragments = new List<Expression>();
    MemberExpression lastSourceFragment = expressionFragments.Last();

    ParameterExpression instanceParameter = Expression.Variable(typeof(object), "instance");
    ParameterExpression resultVariable = Expression.Variable(typeof(TResult), "result");

    LabelTarget useDefaultLabel = Expression.Label("useDefault");
    LabelTarget returnLabel = Expression.Label("return");

    foreach (MemberExpression sourceFragment in expressionFragments)
    {
        MemberInfo memberInfo = (MemberInfo)sourceFragment.Member;

        MemberExpression member = ConstantExpression.MakeMemberAccess(
            Expression.Convert(instanceParameter, memberInfo.DeclaringType),
            memberInfo);

        if (sourceFragment != lastSourceFragment)
        {
            // Iterate into the member.
            targetFragments.Add(
                Expression.Assign(
                    instanceParameter,
                    Expression.Convert(member, typeof(object))));

            // Value types cannot represent a null reference.
            if (sourceFragment.Type.IsClass)
            {
                targetFragments.Add(
                    Expression.IfThen(
                        Expression.Equal(instanceParameter, Expression.Constant(null)),
                        Expression.Goto(useDefaultLabel)));
            }
        }
        else
        {
            // The value of the final member is the actual result.
            targetFragments.Add(
                Expression.Assign(
                    resultVariable,
                    Expression.Convert(member, typeof(TResult))));
        }
    }

    targetFragments.Add(Expression.Goto(returnLabel));
    targetFragments.Add(Expression.Label(useDefaultLabel));
    targetFragments.Add(Expression.Assign(resultVariable, Expression.Default(typeof(TResult))));
    targetFragments.Add(Expression.Label(returnLabel));

    targetFragments.Add(resultVariable);

    Expression finalExpression = Expression.Block(new[] { resultVariable }, targetFragments);

    return Expression.Lambda<Func<TSource, TResult>>(finalExpression, instanceParameter).Compile();
}

The expressionFragments argument contains the fragments of the expression, which according to the examples above would be Customer, Address and ZipCode. You can inspect the finalExpression variable by setting a breakpoint on the last statement, starting a debug session (in the sample project you can use the Debug Test or Debug All Tests In Solution options on the Visual Studio toolbar), hovering over finalExpression when the breakpoint is hit and finally clicking on the magnifying glass next to DebugView. That will show you a textual representation of the expression tree, for example:

.Block(System.Int32 $result) {
    $instance = (System.Object)((NullSafeExpressionSampleTests.TestDoubles.TestEntity)$instance).Property1;
    .If ($instance == null) {
        .Goto useDefault { }
    } .Else {
        .Default(System.Void)
    };
    $instance = (System.Object)((NullSafeExpressionSampleTests.TestDoubles.TestEntityPropertyType)$instance).Property3;
    .If ($instance == null) {
        .Goto useDefault { }
    } .Else {
        .Default(System.Void)
    };
    $result = (System.Int32)((System.String)$instance).Length;
    .Goto return { };
    .Label
    .LabelTarget useDefault:;
    $result = .Default(System.Int32);
    .Label
    .LabelTarget return:;
    $result
}

This nicely explains what happens behind the scenes. As you can see, there is no reflection in the works so performance is solid.

As you can see in the implementation of CreateSafeGetDelegate, the expression tree is compiled to a delegate. This allows for very fast execution. In the full source code, a caching mechanism is implemented so that the expression tree is built and compiled only once, that is at the first time the expression is used.

You can look at or download the sample project source code here.

Conclusion

To sum it all up, to perform expressions without the need to check each property for null, we can now either write (I’ll stick with practical examples here)

  string zipCode = NullSafeExpressionHandler.SafeGet(customer,
    n => n.Address.ZipCode);

or

  string zipCode = customer.NullSafeGet(n => n.Address.ZipCode);

in case we use an appropriate extension method that extends the type the customer variable is of.

All you need to do to get this working is:

  • take the NullSafeExpressionHandler.cs class file from the source code download and place it in your project
  • change the namespace of the class to something appropriate for your project
  • do the same for any provided extension method you’d like to use or create your own extension method
  • make sure the namespace of the extension method(s) is imported at the class where you are going to use them

If you don't want to use this in your project now but play a around with it a bit, I suggest you download the sample project, set breakpoints at some places of interest and the use the 'Debug All Test In Solution' button from the tool bar in Visual Studio.

I want to thank Jon Skeet and Marc Gravell for helping me out on Stack Overflow with two issues I ran into.

kick it on DotNetKicks.com Shout it

Tags:

C#

Comments

8/7/2010 7:43:01 AM #

Andrew

Did I miss something?  because it still looks like it will throw exceptions if null parameters are passed in.

Could you give an example of using the final version?

Andrew Australia |

8/7/2010 10:59:15 AM #

sandord

Hi Andrew, thanks for asking. Well you see, you could use either "string zipCode = NullSafeExpressionHandler.SafeGet(customer, n => n.Address.ZipCode);" or "string zipCode = customer.NullSafeGet(n => n.Address.ZipCode);" when using an extension method that extends the type of the 'customer' variable. You could use the extension method that extends object provided above for that but I recommend choosing a more specific base class.

In the implementation of 'CreateSafeGetDelegate' shown above, you can see that the "Expression.Equal(instanceParameter, Expression.Constant(null))" expression is added to the tree, which does the null checking. You can see this as well in the textual representation of the final expression tree: "# .If ($instance == null) { .Goto useDefault { } ".

Did you download the sample project already? You might want to start with setting a few breakpoints at interesting places and use the 'Debug All Tests In Solution' button on the toolbar in Visual Studio.

sandord Netherlands |

8/7/2010 10:59:41 PM #

sandord

I've added a conclusion to the article for clarity.

sandord Netherlands |

8/11/2010 11:46:36 PM #

Marcel Veldhuizen

Interesting read. I agree that those conditionals can get really annoying. I'm not entirely sure if I find them annoying enough to use something like this in a large scale application which other people would have to maintain, but I'll give it a shot in a hobby project or something. I need to get up to speed on expression trees a little anyway Smile

Marcel Veldhuizen Netherlands |

8/13/2010 1:27:39 AM #

trackback

Subspace .NET Blog | Getting rid of null checks in property chains with - but not limited to - AutoMapper and S#arpArchitecture

Thank you for submitting this cool story - Trackback from DotNetShoutout

DotNetShoutout |

8/13/2010 6:00:26 PM #

Sandor Drieënhuizen

For those who have downloaded the sample code before reading this: I fixed a bug that caused the expression caching not to work properly. You can update from Git or download the latest version at the link provided in the article.

Sandor Drieënhuizen Netherlands |

Comments are closed

About the author

My name is Sandor Drieënhuizen and I'm a passionate and self employed .NET oriented developer, piano player and seeker of the non-obviousness.

Month List