A gotcha for those Fluent interfaces

Published on 2010-4-23

Here is an interesting one that I posted up on Pastebin a few days ago, but has come as a surprise to anybody I've pointed it out to.

When building strongly typed fluent interfaces for any purpose, it has become increasingly common to abuse expressions in order to retrieve members with a utility like so:

myFunkyInterface.GetProperty(x=>x.SomeProperty);

This can then be used in place of magic strings and name changes can then be caught by compile time errors instead of test-time exceptions.

The lambda based GetProperty method will typically look something like this:

MemberInfo GetProperty<Tpoco, TReturn>(Expression<Func<TPoco, TReturn>> expression)
{
         MemberExpression memberExpression = (MemberExpression)expression.Body;
         return memberExpression.Member;
}

The expression passed in is a member expression, and we can therefore retrieve the member from that member expression and use it as is.

This is a replacement of the traditional non-lambda method which would look something like this:

MemberInfo member = typeof(SomeClass).GetProperty("SomeProperty");

Consider the following class structure:

public class SomeBaseClass
{
      public  virtual string SomeProperty { get;set; }
}

public class SomeClass : SomeBaseClass
{
      public override string SomeProperty { get; set;} 
}
  
Calling:
MemberInfo genericMember = ReflectionHelper.GetMember<SomeClass>(x=>x.SomeProperty);
MemberInfo traditionalMember = typeof(SomeClass).GetProperty("SomeProperty");

Will result in two completely different objects.

Checking the DeclaringType property on the returned members yields in an answer:

  • genericMember.DeclaringType:  SomeBaseClass
  • traditionalMember.DeclaringType: SomeClass

Seems like a small thing, but as any framework developer will tell you, when writing any set of generic interfaces, you always need a set of non-generic interfaces for dynamic invocation of your framework.

It's helpful if they do the same thing.


The solution of course, is to make the generic method use the non-generic method under the hood and although you already have the member, not use it directly.

MemberInfo GetProperty<TPoco, TReturn>(Expression<Func<TPoco, TReturn>> expression)
{
         MemberExpression memberExpression = (MemberExpression)expression.Body;
        return typeof(Tpoco).GetProperty(memberExpression.Member.Name);
}

I assumed that the output of these two alternate methods would be the same, but it turns out that this assumption was going to introduce a bug into my project that was quite hard to track down!

Of course, if I'd written the code as I do now, I'd have written all the non-generic interfaces first, and then written the generic interfaces on top of that with their implementations just calling the non-generic  interfaces and therefore not ran into the problem because they'd both be using the same underlying code.

Still, good to know I guess,  thankfully my tests caught this one before I released any code that would have caused problems.

2020 © Rob Ashton. ALL Rights Reserved.