2012-09-20 102 views
1

我有一个lambda表达式树问题,我找不出来。我试图做一个动态的LINQ Select语句。动态lambda选择表达式

我有一个动态库在这里创建:

private static dynamic GetRepository(Type type) 
{ 
    dynamic repository = typeof(IFactory).GetMethod("Create").MakeGenericMethod(typeof(IRepository<>).MakeGenericType(type)).Invoke(ObjectFactory.Instance, new object[] { }); 
    return repository; 
} 

有了这个,我需要调用这个只有我不知道在编译时x和SomeProperty。我有PropertyInfo propertyInfo与SomeProperty名称和类型objectType与x类型。 它未能在目标1与此异常:

System.Reflection.AmbiguousMatchException在GetMethod(字符串名称)

代码:

private SomeObject CreateSomeObject (PropertyInfo propertyInfo, Type objectType) 
{ 
    var param = Expression.Parameter(objectType, "x"); 
    MemberExpression expression = Expression.PropertyOrField(param, propertyInfo.Name); 

    //Goal 1: var selectExpression = Expression.Lambda<Func<objectType, object>>(expression, param); 
    var selectExpression = typeof(Expression).GetMethod("Lambda").MakeGenericMethod(typeof(Func<,>) 
    .MakeGenericType(objectType, typeof(object))) 
    .Invoke((object)null, new object[] { expression, param }); 

    // Goal 2: List<object> list = GetRepository(objectType).FindAllQuery().Select(x => x.SomeProperty).ToList(); 
    List<object> list = GetRepository(objectType).FindAll().Select(selectExpression); 
} 

如何解决这个问题?

更新1:

我已经改变的方式来选择LAMBDA方法,所述方法收拾“PARAM”参数和我添加了一个对象转换到“表达”。

private SomeObject CreateSomeObject (PropertyInfo propertyInfo, Type objectType) 
{ 
    var param = Expression.Parameter(objectType, "x"); 
    Expression expression = Expression.Convert(Expression.PropertyOrField(param, propertyInfo.Name), typeof(object)); 

    //Goal 1: var selectExpression = Expression.Lambda<Func<objectType, object>>(expression, param); 
    var selectExpression = typeof(Expression).GetMethods().First(m => m.Name == "Lambda" && m.IsGenericMethod) 
    .MakeGenericMethod(typeof(Func<,>) 
    .MakeGenericType(objectType, typeof(object))) 
    .Invoke((object)null, new object[] { expression, new [] { param }}); 

    // Goal 2: List<object> list = GetRepository(objectType).FindAllQuery().Select(x => x.SomeProperty).ToList(); 
    List<object> list = GetRepository(objectType).FindAll().Select(selectExpression); 
} 

但知道我会在目标2(Microsoft.CSharp.RuntimeBinder.RuntimeBinderException)此异常:

'System.Collections.Generic.List' 不包含一个定义 “选择'

这部分是对的,因为它在System.Linq中定义,它是一个扩展方法。我如何得到这个工作?

回答

3

抛出异常的代码是

typeof(Expression).GetMethod("Lambda") 

,因为有18层的方法称为LambdaExpression类型(因此AmbiguousMatchException)所定义。

GetMethod(string methodName)适用于没有过载的情况。在这种情况下,我会使用GetMethods(),然后筛选出我需要的。

在你的情况,正确的过载是

Expression.Lambda<TDelegate>(Expression body, params ParameterExpression[] parameters) 

您可以编写一个通过检查的参数和它们的类型数量验证正确的过载的功能,但我发现了一个更简便的方法:通过过滤方法在.ToString()表示,这在我们的情况是:

System.Linq.Expressions.Expression`1[TDelegate] Lambda[TDelegate](System.Linq.Expressions.Expression, System.Linq.Expressions.ParameterExpression[]) 

还有你传递的参数(new object[] { expression, param })的方式有问题。第二个参数不是类型​​,而是ParameterExpression[](数组),因此您应该通过new[]{param}而不仅仅是param。当用普通代码调用它时,它的工作原理就是这样,因为它定义为params ParameterExpression[]

总之,下面的代码应该工作你的情况:

const string methodSignature = 
    "System.Linq.Expressions.Expression`1[TDelegate] Lambda[TDelegate]" + 
    "(System.Linq.Expressions.Expression, System.Linq.Expressions.ParameterExpression[])"; 

var lambdaMethod = typeof (Expression).GetMethods() 
    .Single(mi => mi.ToString() == methodSignature); 

var funcType = typeof (Func<,>).MakeGenericType(objectType, typeof (object)); 

var genericLambda = lambdaMethod.MakeGenericMethod(funcType); 

var selectExpression = genericLambda.Invoke(null, new object[] { expression, new[] { param } }); 
+0

实际上[有18个方法叫做'Lambda'上'Expression'(http://msdn.microsoft.com/en- us/library/system.linq.expressions.expression.lambda.aspx),其中6个是通用的。所以,你的代码将返回一个通用的代码,但我认为它很可能不会是正确的。 – svick

+0

谢谢..现在我得到:“System.Linq.Expressions.TypedParameterExpression”类型的对象不能转换为类型“System.Linq.Expressions.ParameterExpression []”。在调用 方法。 –

+0

@svick:那么我该如何选择正确的? –