Lets say I have a class
public class MyClass
{
  public int MyProperty{get;set;}
}
and I want to map property in strogly-typed manner. So instead of typing
MapField("MyProperty", "ColumnName");
I want to have
MapField(x=>x.MyProperty, "ColumnName");
This gives a nice opportunity to refactor, as when renaming MyProperty to something else you will rename it in the mapping as well (though R# can do that for you as it can search literals with property name when renaming a property).
So we need to determine what's the name of property we're passing and also might be interested in it's type.
MapField method should have this signature to work
public void MapField(Expression<Func<MyClass, object>> expression, string columnName)
{
  //get field/property name and type from expression
}
Now we have to parse that linq expression. To get required info I've wrote a class with following interface
public interface ILinqExpressionReader<T>
{
string GetFieldOrPropertyNameFromBodyOfExpression(Expression<Func<T, object>> expression);
Type GetFieldTypeFromBodyOfExpression(Expression<Func<T, object>> expression);
}
and here is the implementation:
public class LinqExpressionReader<T> : ILinqExpressionReader<T>
{
public string GetFieldOrPropertyNameFromBodyOfExpression(Expression<Func<T, object>> expression)
{
   var memberInfo = GetMemberInfoFrom(expression);
   return memberInfo.Name;
}
private static MemberInfo GetMemberInfoFrom(Expression<Func<T, object>> expression)
{
   MemberInfo member;
   if (expression.Body.GetType() == typeof(UnaryExpression))
   {
       var unaryExpression = (UnaryExpression)expression.Body;
       if (IsMemberExpression(unaryExpression.Operand))
       {
           member = ((MemberExpression)unaryExpression.Operand).Member;
       }
       else { throw new ArgumentOutOfRangeException("expression", "Should use the following syntax: x => x.PropertyName (or x => x.FieldName)"); }
   }
   else if (IsMemberExpression(expression.Body))
   {
       member = ((MemberExpression)expression.Body).Member;
   }
   else { throw new ArgumentOutOfRangeException("expression", "Should use the following syntax: x => x.PropertyName"); }
   return member;
}
private static bool IsMemberExpression(Expression expression)
{
   return expression.GetType() == typeof(MemberExpression);
}
public Type GetFieldTypeFromBodyOfExpression(Expression<Func<T, object>> expression)
{
   var memberInfo = GetMemberInfoFrom(expression);
   switch (memberInfo.MemberType)
   {
       case MemberTypes.Property:
           return ((PropertyInfo)memberInfo).PropertyType;
       case MemberTypes.Field:
           return ((FieldInfo)memberInfo).FieldType;
       default:
           throw new Exception("Cannot get types for anything but Properties and Fields");
   }
}
}
Implementation uses reflection to get info that is required. Also works fine with properties or fields of Enum type.
 
No comments:
Post a Comment