将编译的Func方法添加到一起的目的是什么?

时间:2013-03-30 18:33:30

标签: c# linq lambda compiled

我已经看到可以将编译的方法一起添加。

Expression<Func<Customer, bool>> ln = c => c.lastname.Equals(_customer.lastName, StringComparison.InvariantCultureIgnoreCase);
Expression<Func<Customer, bool>> fn = c => c.firstname.Equals(_customer.firstName, StringComparison.InvariantCultureIgnoreCase);
Expression<Func<Customer, bool>> fdob = c => c.DOB.ToString("yyyyMMdd").Equals(_customer.DOB.ToString("yyyyMMdd"));

var filter = ln.Compile() + fn.Compile() + fdob.Compile();

这样做有意义吗?

我打算使用过滤器代替lambda表达式来过滤客户的存储库:

IEnumerable<Customer> customersFound = _repo.Customers.Where(filter);

根据业务逻辑,我可能会也可能不会将三个编译的方法一起添加,但是选择,并且可能会添加更多已编译的方法。

任何人都可以解释将它们一起添加会不会构建查询字符串?有人有更好的建议吗?

我可以链接“Where”语句并使用常规的lambda表达式,但我对你可能从编译方法中获得的内容感兴趣并添加它们!

3 个答案:

答案 0 :(得分:7)

这是Compile方法返回委托这一事实的副作用。

在内部,委托是多播委托,它可能涉及对方法的多个链式引用。

在这种情况下,+只是将它们链接在一起的快捷方式。您可以在MSDN上的How to: Combine Delegates中阅读有关组合代理的更多信息。

这是一个LINQPad程序,演示了:

void Main()
{
    var filter = GetX() + GetY() + GetZ();
    filter().Dump();
}

public int X() { Debug.WriteLine("X()"); return 1; }
public int Y() { Debug.WriteLine("Y()"); return 2; }
public int Z() { Debug.WriteLine("Z()"); return 3; }

public Func<int> GetX() { return X; }
public Func<int> GetY() { return Y; }
public Func<int> GetZ() { return Z; }

输出:

X()
Y()
Z()
3

请注意,它似乎只是忽略了第一个方法调用的返回值,并且只返回最后一个,尽管它也完全调用了先前的方法。

请注意,上面的代码与此类似:

void Main()
{
    Func<int> x = X;
    Func<int> y = Y;
    Func<int> z = Z;

    var filter = x + y + z;
    filter().Dump();
}

public int X() { Debug.WriteLine("X()"); return 1; }
public int Y() { Debug.WriteLine("Y()"); return 2; }
public int Z() { Debug.WriteLine("Z()"); return 3; }

简短的回答是,不,这不符合你的意愿。

答案 1 :(得分:5)

当您致电Compile()时,您正在创建Func<Customer, bool>类型的实例,这是一个代表。

委托重载operator+以返回多播代理,这就是这里发生的事情。

请参阅MSDN:How to: Combine Delegates (Multicast Delegates)

所以,不 - 将它们加在一起不会构建查询字符串。


如果是EF,则需要使用表达式树,而不是Lambdas。

您可以使用System.Linq.Expressions命名空间创建和修改表达式树:

MSDN:System.Linq.Expressions Namespace

MSDN:How to: Use Expression Trees to Build Dynamic Queries

答案 2 :(得分:3)

将谓词链接起来是一种创造性的努力,太糟糕了,它不起作用。 Lasse和Nicholas(+2)非常好地解释了原因。但你也问:

  

有人有更好的建议吗?

编译表达式的缺点是它们不再是表达式,因此不能与SQL查询提供程序支持的IQueryable一起使用(例如linq to sql或linq to entities)。我认为_repo.CustomersIQueryable

如果您想动态链接表达式,LINQKit's PredicateBuilder是do this的绝佳工具。

var pred = Predicate.True<Customer>();

pred = pred.And(c => c.lastname.Equals(_customer.lastName, StringComparison.InvariantCultureIgnoreCase);
pred = pred.And(c => c.firstname.Equals(_customer.firstName, StringComparison.InvariantCultureIgnoreCase);
pred = pred.And(c => c.DOB.ToString("yyyyMMdd").Equals(_customer.DOB.ToString("yyyyMMdd"));

var customersFound = _repo.Customers.Where(pred.Expand());

这是Expand的用途:

  

实体框架的查询处理管道无法处理调用表达式,这就是您需要在查询中的第一个对象上调用AsExpandable的原因。通过调用AsExpandable,可以激活LINQKit的表达式访问者类,该类使用Entity Framework可以理解的更简单的结构替换调用表达式。

或者:没有它,表达式为Invoke d,这会导致EF中出现异常:

  

LINQ to Entities中不支持LINQ表达式节点类型“Invoke”。

顺便说一句。如果你使用linq到sql / entities,你也可以使用c.lastname == _customer.lastName,因为它被翻译成SQL,数据库整理将决定区分大小写。