Exploring ShadeTree Features, Part 1: Static Reflection with ReflectionHelper


At work, Jeremy and I have been using — and contributing to — some of the code he’s put together over the past few years.  He’s created the ‘ShadeTree’ project which is currently housed in the Storyteller Project source repository. I’d like to do a few blog posts to share with you some of the really neat gems in this project that you can you use in your projects to help accelerate your development.

The first thing I’d like to cover in ShadeTree is the ReflectionHelper class. ReflectionHelper aids with static reflection and the elimination of many ‘magic strings’.  Static reflection is a process I first became aware of on Daniel Cazzulino’s blog post “Statically-typed Reflection with Linq.”  With the new Expression Trees feature of .NET 3.5, there are some interesting things you can do that aren’t necessarily query-related.  The subject of Expression Trees is a very complicated one, but they are very powerful and worth some cursory investigation.  Fortunately, you can “dip your toe” into Expression Trees without understanding how everything works and build up from there (at least, that’s what I’m doing, so there!).

First, let’s start with a problem…

Classic Reflection with Magic Strings

Let’s pretend we had an object-to-object mapping problem. Let’s say that we have some sort of web service which returns a simple DTO (data transfer object — a flattened, simple object usually used for the purpose of communicating between two different systems with different models).  So we want to take our rich domain model object graph and flatten it into a DTO in order to send to our remote client. Let’s also say that we have a lot of these situations and that it’s going to be too much work to write one-off mapping functions for each scenario.  We’ve decided we need to build a basic object-to-object mapper that can handle common problems like nulls, string conversion, etc. Please don’t get hung up on these details, the point is: We need a scenario that involves some sort of reflection. Please use your own imagination if my scenario isn’t working for you.

Consider this simple model with the red lines representing the intended mapping from the model to the DTO:

CustomerDTODiagram 

In the past, we may have pulled out our Mighty Hammer of XML +1 for this problem and ended up with something like this:

<DtoMap dtoClass="SomeProject.CustomerDTO, SomeProject" 
        srcRootClass="SomeProject.Domain.Customer, SomeProject">

    <DtoProperty name="Name" mapsToSrcProperty="Name"/>

    <DtoProperty name="SiteName">
        <DtoSourcePropertyMap propretyOnSourceObject="Site" propertyToAccess="Name"/>
    </DtoProperty>

    <DtoPropety name="PostalCode">
        <DtoSourcePropertyMap propretyOnSourceObject="Site">
            <DtoSourcePropertyMap propretyOnSourceObject="PrimaryAddress" propertyToAccess="PostalCode"/>
        </DtoSourcePropertyMap>
    </DtoPropety>
    
</DtoMap>

This would probably work with a little tweaking, but the fundamental problem with this is: Magic strings everywhere!  Our refactoring tools are now likely defeated.  If we ever rename the ‘Site’ property on ‘Customer’, it’s unlikely most refactoring tools would know to change this XML map appropriately.  It’s possible we’d catch this and other similar problem through tests, but it’s just more work and more friction that we don’t need (in this case, at least).  One other problem with this XML is that it’s very verbose, there’s lots of  language overhead here (extra angle brackets, etc). In general, it’d be nice to stick to code if we can (with nice, safe compilers to keep us on the straight and narrow).

You could accomplish this in other ways (without XML). But the code ends up looking something like this:

Type srcType = Type.GetType("SomeProject.Domain.Customer, SomeProject");
PropertyInfo nameProp = srcType.GetProperty("Name");

This code has much of the same problem that the XML-based mapping syntax has (i.e. some refactoring tools may not pick up changes, etc).

Magic strings cause problems, let’s face it. So what can we do, instead?

Static Reflection with Expression Trees and ShadeTree.Core.ReflectionHelper

With static reflection we can build maps in code that reference the types and members directly. This is made easier using ShadeTree.Core.ReflectionHelper.

First, let me show you what our end-result mapping code might look like now that we can use Static Reflection:

DtoMap<CustomerDto> map = new DtoMap<CustomerDto>();

map.AddPropertyMap(
    new DtoMapProperty<CustomerDto, Customer>
        {
            DtoPropertyReference = (dto => dto.Name),
            SourcePropertyReference = (src => src.Name)
        }
    );

map.AddPropertyMap(
    new DtoMapProperty<CustomerDto, Customer>
        {
            DtoPropertyReference = (dto => dto.SiteName),
            SourcePropertyReference = (src => src.Site.Name)
        }
    );

map.AddPropertyMap(
    new DtoMapProperty<CustomerDto, Customer>
        {
            DtoPropertyReference = (dto => dto.PostalCode),
            SourcePropertyReference = (src => src.Site.PrimaryAddress.PostalCode)
        }
    );

 

Not bad, but pretty verbose.  Maybe we could tighten it up a bit with a Fluent API:

var map = new DtoMap<CustomerDto, Customer>()
    .Property(dto => dto.Name).ToSource(src => src.Name)
    .Property(dto => dto.SiteName).ToSource(src => src.Site.Name)
    .Property(dto => dto.PostalCode).ToSource(src => src.Site.PrimaryAddress.PostalCode);

Now, that’s pretty funky, huh? It threw me for a loop when I first saw that.  My first thought was: “But src=>src.Name isn’t a valid expression in this context! You can’t just reference a member without an assignment, for the compiler tells me so!”

You might, rightly, expect to get this compile error:

error CS0201: Only assignment, call, increment, decrement, 
and new object expressions can be used as a statement

So why don’t you?  Well, the trick is that your method or property must take a parameter of type Expression<Func<T,object».  Not just a System.Func<T,object>, but you have to have the Expression<> around it. This causes the C# compiler to behave strangely — namely it does not treat the code inside the expression as executable code, but as a parsed language element for LATER evaluation.  “dto.Name” is a valid expression in certain contexts, so the compiler allows it as an Expression<>. If you later try to execute/invoke that expression, THEN you’ll get an error similar to the CS0201 above.  But for right now, the compiler lets it pass, and will generate the IL to pass your method or property a type of Expression<>.

UPDATE: (HT to commenter Paul Batum for catching me on this): Lambda expressions have an implied “return” statement in them, so they will compile *and execute* without the CS0201 issue because they are not a pure member access expression they are a return statement combined with a member access expression.

What can you do with this Expression<> type?  Well, lots of things, but that’s a much larger topic. For right now, you can pass it to the ShadeTree ReflectionHelper and request to get a PropertyInfo from it.  Here, let me show you.  Remember our mapping Fluent API up above? Here’s a crude start on what the DtoMap<DTO, SRC> class might look like:

public class DtoMap<DTO, SRC>
{
    private PropertyInfo _lastDtoProperty;

    public DtoMap<DTO, SRC> Property(Expression<Func<DTO, object>> expression)
    {
        _lastDtoProperty = ReflectionHelper.GetProperty(expression);
        return this;
    }
}

So I have an Expression<> of a lambda (Func<T,object>). That lambda currently holds an expression which can be evaluated into a direct member reference (i.e. the Name property).  We can use some LINQ stuff under the covers to turn that Expression<> into a .NET reflection element: a PropertyInfo.  We’ve now accomplished the same thing as someType.GetProperty(“Name”), but without any magic strings!

Now, your refactoring tool should be able to directly pick up the dto.Name reference and, if you ever change the name of the Name property, change that reference for you automatically.

Deep Access Static Reflection and the Accessor interface

We have a big problem with that last code sample, though.  GetProperty() will explode if you pass it something like src.Site.Name (n-level deep property accessors).   ReflectionHelper, fortunately, has a way of dealing with this. It will create the accessor graph for you and return to you the path it took from ‘dto’ to ‘Name’ and add some nifty convenience in there for you as well!  There’s a method on ReflectionHelper called ‘GetAccessor()’.  It’s very similar to GetProperty(), but it handles deep accessor paths.

Let’s look at the ‘ToSource()’ method of our DtoMap now.  ToSource is going to need this deep access because our second call to it uses ‘src.Site.Name’ which would cause GetProperty() to throw an exception.   Here’s the updated DtoMap source with new Accessor goodness:

public class DtoMap<DTO, SRC>
{
    private Accessor _lastDtoAccessor;
    private Accessor _lastSrcAccessor;

    public DtoMap<DTO, SRC> Property(Expression<Func<DTO, object>> expression)
    {
        _lastDtoAccessor = ReflectionHelper.GetAccessor(expression);
        return this;
    }

    public DtoMap<DTO, SRC> ToSource(Expression<Func<SRC, object>> expression)
    {
        _lastSrcAccessor = ReflectionHelper.GetAccessor(expression);
        return this;
    }
}

You should notice that I changed the Property() method to use GetAccessor and I’ve added the new ToSource() method which uses GetAccessor as well.

The ShadeTree.Core.Accessor interface gives us some extra information above and beyond just the straight PropertyInfo as well as two very important methods: SetValue() and GetValue().  These methods expect an object of the root type (in our case, Customer) and will navigate the graph to get or set the value on the property on the object in which we’re interested.  For example, if I have my _lastSrcAccessor we just built in our ToSource() method, we can call GetValue() and pass in a Customer object and get, for example, it’s Site’s Name property.  Consider this example:

var customer = _customerRepository.GetCustomer(9);
customer.Site.Name = "NewSiteName";

string ourName = (string) _lastSrcAccessor.GetValue(customer);

// the _ourName_ variable will now equal "NewSiteName"

 

So Accessor.GetValue() will walk down the path from Customer to Site and from Site to Name and return the Name property value.  If any value in the chain is null, GetValue() will return null.  This is the current behavior, if you don’t like it, please submit a patch (take a look at the ShadeTree.Core.PropertyChain implementation for starters).

Conclusion

There’s one other thing on ReflectionHelper I didn’t mention: GetMethod() which allows you to pass an Expression that references a method and get a MethodInfo back from it so you can use static reflection on more than just properties.  There are a few other things on Accessor that I didn’t mention either that I think are worth you giving a glance at.

I plan on doing a few more of these ShadeTree posts, so please check back later.

Example of Removing Some Pain: Grid Fluent API