【问题标题】:Composing invocations with Expression<Func<T,bool>> the same way as Func<T,bool>使用 Expression<Func<T,bool>> 组合调用,方式与 Func<T,bool> 相同
【发布时间】:2017-05-20 16:56:54
【问题描述】:

考虑一个可以用作多个其他类的成员的类:

class Customer {
    public string FirstName {get;set;}
    public string LastName {get;set;}
}
// Both "Order" and "Profile" have a "Customer" property
class Order {
    public Customer Customer {get;set;}
}
class Profile {
    public Customer Customer {get;set;}
}

我想定义一个方法来检查与Customer 关联的对象。如果我想要一个内存检查器,我会这样做:

static Func<T,bool> Check<T>(Func<T,Customer> conv, string first, string last) {
    return obj => conv(obj).FirstName == first && conv(obj).LastName == last;
}

我可以将我的检查器用于内存中的序列,如下所示:

var matchingOrders = orders
    .Where(Check<Order>(x => x.Customer, "Foo", "Bar"))
    .ToList();
var matchingProfiles = profiles
    .Where(Check<Profile>(x => x.Customer, "Foo", "Bar"))
    .ToList();

现在我想对 Expression&lt;Func&lt;T,bool&gt;&gt; 做同样的事情:

static Expression<Func<T,bool>> Check<T>(Expression<Func<T,Customer>> conv, string first, string last)

不幸的是,同样的技巧不起作用:

return obj => conv(obj).FirstName == first && conv(obj).LastName == last;

并像这样使用它:

var matchingOrders = dbContext.Orders
    .Where(Check<Order>(x => x.Customer, "Foo", "Bar"))
    .ToList();
var matchingProfiles = dbContext.Profiles
    .Where(Check<Profile>(x => x.Customer, "Foo", "Bar"))
    .ToList();

这会触发错误:

CS0119:表达式表示应使用 variable', where amethod 组

我可以像编写委托一样编写表达式吗?

【问题讨论】:

    标签: c# linq lambda linq-expressions


    【解决方案1】:

    不幸的是,C# 目前不提供从Expression&lt;Func&lt;...&gt;&gt; 对象组合表达式的方法。你必须使用表达式树,这要长一些:

    static Expression<Func<T,bool>> CheckExpr<T>(Expression<Func<T,Customer>> conv, string first, string last) {
        var arg = Expression.Parameter(typeof(T));
        var get = Expression.Invoke(conv, arg);
        return Expression.Lambda<Func<T,bool>>(
            Expression.MakeBinary(
                ExpressionType.AndAlso
            ,   Expression.MakeBinary(
                    ExpressionType.Equal
                ,   Expression.Property(get, nameof(Customer.FirstName))
                ,   Expression.Constant(first)
                )
            ,   Expression.MakeBinary(
                    ExpressionType.Equal
                ,   Expression.Property(get, nameof(Customer.LastName))
                ,   Expression.Constant(last)
                )
            )
        ,   arg
        );
    }
    

    【讨论】:

    • 实际上,您可以采用两个类似谓词的表达式树,然后使用树遍历来创建一个 &&-combined 或 ||-combined predicate-like copy-copy,因为 IIRC 你不能只重用已经绑定在该树中的原始那些(参数表达式将在树叶处阻止它)。如果部分采用相同的参数,那就更容易了。我已经看到它已经实现,有人创建了一个通用表达式树访问者,作为样本之一,有一个通过 OR 运算符组合 where 子句的示例.. 啊,我现在只是找到合适的词来谷歌它:/
    • 哈哈!找到了!当然,它很旧,2008 年左右,但这样的东西不会生锈! Please see this article。它使用来自 another article 的 ExpressionVisitor 来构建 Compose.And().Or() 扩展方法 - 特别是,请查看第一篇文章中的最后一个示例,看看它是如何可重用和方便的。 (顺便说一句,现在已经 9 年了,这仍然不在 BCL 中..)
    • 嗯..不好意思问一下,但我今天可能已经用完了所有的空闲时间:/也许你可以找点时间把这些文章中的所有代码位一起收集起来?作为同一问题的另一个答案。如果这个实用程序最终从互联网上消失,那将是一种耻辱
    • @quetzalcoatl 有点晚了,但我相信LINQKit 拥有所需的一切,并且很容易添加到项目中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-09
    • 1970-01-01
    • 1970-01-01
    • 2010-10-02
    相关资源
    最近更新 更多