Tuesday, August 4, 2009

ValidateLambdaArgs and problems with casting linq Expression

Was stuck with a problem while implementing strogly typed mappings via linq expressions for a while. The problem was with passing Expression into method with reflection.

typeof(SomeClassRepository)
.GetMethod("LoadBy")
.Invoke(repository, new object[] { lambdaExpression, valueToMatch });
Actual signature of LoadBy looks like this:
public SomeClass LoadBy(Expression<Func<SomeClass, object>> expression, string value)
I want to build expression like x=>x.SomeProperty and pass it to LoadBy via reflection. The problem however arise its head when trying to pass value types when building expression:

var intPropertyName = "IntProperty";

var type = typeof(SomeClass);
var parameterExpression = Expression.Parameter(type, "x");
var memberExpression = Expression.Property(parameterExpression, intPropertyName);
var delegateType = typeof(Func<,>).MakeGenericType(new Type[] { type, typeof(object) });

var lambdaExpression = Expression.Lambda(delegateType,memberExpression, new[] { parameterExpression });

Here's definition of SomeClass:
public class SomeClass
{
public int IntProperty { get; set; }
}
Building such lambda expression raises System.ArgumentException in System.Linq.Expressions.Expression.ValidateLambdaArgs. Exception will say that you cannot use System.Int32 for return type System.Object.

To avoid this I found no way other than to use more reflection stuff and make a hack. Building lambda expression with following code instead of Expression.Lambda(...) fixed the problem:

var onlys = new ReadOnlyCollection<ParameterExpression>(new List<ParameterExpression> { parameterExpression });
var specificType = typeof(Expression<>).MakeGenericType(new System.Type[] { delegateType });
var lambdaExpression = Activator.CreateInstance(specificType,
BindingFlags.NonPublic | BindingFlags.Instance,
null,
new object[] { memberExpression, onlys },
null);

No comments:

Post a Comment