【问题标题】:Use lambda expression in another lambda expression在另一个 lambda 表达式中使用 lambda 表达式
【发布时间】:2014-10-17 10:37:04
【问题描述】:

我需要以第二个表达式“包含”第一个表达式的方式组合两个 lambda 表达式。 我搜索了很多,但没有找到任何明确的答案......

我想要做的是以下内容: 第一个表达式“expression1”作为参数传递给方法,并简单地用于定义第二个 lambda 必须对哪个字段或属性进行操作。

从原理上讲,我正在尝试执行以下操作:

// simple field selector :
Expression<Func<T, string>> expression1 = obj => obj.field; 

// trying to use the field selector in 2nd expression :
Expression<Func<T, bool>> e2 = obj => [[Result of expression1]].Equals("myValue");

换句话说,我想得到:

Expression<Func<T, bool>> e2 = obj => obj.field.Equals("myValue"); 

我需要这样做,因为并不总是会调用 Equals() 方法,而是会调用许多不同的方法。

我试图将表达式 1 编译为 Func&lt;T, string&gt; 以便在表达式 2 中调用此 Func,但是当我将它与 LinQ 一起使用时,我得到了一个异常,因为 LinQ 不支持调用节点类型。

所以我的问题是:有没有办法将两个表达式的主体结合起来,请问如何?

提前致谢!

【问题讨论】:

    标签: c# linq lambda expression


    【解决方案1】:

    好吧,这应该可以解决问题:

    void Main()
    {
        var execute = Create<TestClass>(
                first => first.MyStringField, // field selector
                second => second.Equals("1234") // method selector
            );
    
        Console.WriteLine(execute(new TestClass("1234"))); // true
        Console.WriteLine(execute(new TestClass("4321"))); // false
    }
    
    class TestClass
    {
        public string MyStringField;
    
        public TestClass(string val){
            MyStringField = val;
        }
    
    }
    
    static Func<TSource, bool> Create<TSource>(
                        Expression<Func<TSource, string>> fieldSelector,
                        Expression<Func<string, bool>> methodSelector
                    )
    {
        // todo: classical validation of fieldSelector, if necessary.
        // todo: classical validation of methodSelector, if necessary.
    
        var compiledFieldSelector = fieldSelector.Compile();
        var compiledMethodSelector = methodSelector.Compile();
    
        return T => compiledMethodSelector(compiledFieldSelector(T));
    }
    

    注意,由于 lambda 表达式的性质,你需要验证字段选择器和方法选择器,否则可能会做一些非常奇怪的事情。

    或者,下一种方法创建不“组合”事物的 lambda,从某种意义上说,这更好,因为 LinqToEntities 不应该有解释查询的问题。

     static Func<TSource, bool> Create<TSource>(
                        Expression<Func<TSource, string>> memberSelector,
                        Expression<Func<string, bool>> methodSelector
                    )
    {
        // todo: classical validation of memberSelector, if necessary.
        // todo: classical validation of methodSelector, if necessary.
    
        var memberExpression = (MemberExpression)(memberSelector.Body);
        var methodCallExpression = (MethodCallExpression)(methodSelector.Body);
    
        // input TSource => ..
        var input = Expression.Parameter(typeof(TSource));
    
        var call = Expression.Call(
                    Expression.MakeMemberAccess(
                                    input, 
                                    memberExpression.Member), 
    
                    methodCallExpression.Method, 
                    methodCallExpression.Arguments);
    
        return Expression.Lambda<Func<TSource, bool>>(call, input)
                    .Compile();
    }
    

    【讨论】:

    • 谢谢克里斯·埃尔玛。您的解决方案在经典语境中效果很好。不幸的是,当我使用 LinQ 时,我在运行时收到以下 System.NotSupportedException:LINQ to Entities 不支持 LINQ 表达式节点类型“Invoke”。
    • @Numer_11:更新帖子。这是你要找的吗?
    • 是的!我测试了你的新答案,但仍然有例外。之后,我删除了 .Compile() 调用,并将 Create 方法的返回类型更改为匹配。最后通过这个小小的修改,它可以与 LinQ 完美配合!多谢 ! (发布修改以使其他人清楚)
    【解决方案2】:

    Chris Eelmaa 的第二个答案是好的,只需删除 .Compile() 调用以避免 Invoke 出现异常:

    static Expression<Func<TSource, bool>> Create<TSource>(
                        Expression<Func<TSource, string>> memberSelector,
                        Expression<Func<string, bool>> methodSelector
                    )
    {
        // todo: classical validation of memberSelector, if necessary.
        // todo: classical validation of methodSelector, if necessary.
    
        var memberExpression = (MemberExpression)(memberSelector.Body);
        var methodCallExpression = (MethodCallExpression)(methodSelector.Body);
    
        // input TSource => ..
        var input = Expression.Parameter(typeof(TSource));
    
        var call = Expression.Call(
                    Expression.MakeMemberAccess(
                                    input, 
                                    memberExpression.Member), 
    
                    methodCallExpression.Method, 
                    methodCallExpression.Arguments);
    
        return Expression.Lambda<Func<TSource, bool>>(call, input);
    }
    

    在我的例子中,它是这样使用的: (selector 是类似 u =&gt; u.id 的表达式,并且 requestIQueryable&lt;T&gt;,两者都作为参数接收)

    Expression<Func<T, bool>> contains = Create<T>(selector, u => u.Contains(searchExpression));
    IQueryable<T> result = request.Where(contains);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-02
      • 1970-01-01
      相关资源
      最近更新 更多