动态lambda选择表达式

时间:2012-09-20 09:56:53

标签: c# .net reflection lambda expression-trees

我有一个lambda表达式树问题,我无法弄清楚。我正在尝试创建一个动态的linq Select语句。

我在这里创建了一个动态存储库:

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名称和Type objectType有x类型。 它在目标1中失败,但有以下例外:

  

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

代码:

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'参数的方法,并且我已经将对象Converter添加到'expression'。

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中定义的,它是一种扩展方法。我如何使这个工作?

1 个答案:

答案 0 :(得分:3)

抛出异常的代码是

typeof(Expression).GetMethod("Lambda")

因为在Lambda类型上定义了18个名为Expression的方法(因此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类型,而是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 } });