【问题标题】:Dynamic lambda select expression动态 lambda 选择表达式
【发布时间】:2012-09-20 09:56:53
【问题描述】:

我有一个无法解决的 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。我有带有 SomeProperty 名称的 PropertyInfo propertyInfo 和带有 x 类型的 Type objectType。 它在目标 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”参数的方式,并添加了一个对象转换器到“表达式”。

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

【问题讨论】:

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


    【解决方案1】:

    抛出异常的代码是

    typeof(Expression).GetMethod("Lambda")
    

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

    【讨论】:

    • 其实there are 18 methods called Lambda on Expression 有6个是通用的。因此,您的代码将返回其中一个通用代码,但我认为它很可能不是正确的。
    • 谢谢.. 现在我得到:“System.Linq.Expressions.TypedParameterExpression”类型的对象无法转换为“System.Linq.Expressions.ParameterExpression[]”类型。在 Invoke 方法处。
    • 请查看原帖中的更新 - 针对该异常的任何解决方案?
    • @MortenHolmgaard .First(m =&gt; m.Name == "Lambda" &amp;&amp; m.IsGenericMethod) 条件是错误的,正如@svick 上面指出的那样。请查看我的答案的更新是否解决了您的问题。
    • 我已经尝试过你的新解决方案,就像在我的“更新 1”中一样,这个接缝可以工作,但是在调用“选择”时我会遇到异常。我得到:'System.Collections.Generic .List '不包含'Select'的定义..请看我的更新..
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多