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

Ian*_*son 7 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();
Run Code Online (Sandbox Code Playgroud)

这样做有意义吗?

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

IEnumerable<Customer> customersFound = _repo.Customers.Where(filter);
Run Code Online (Sandbox Code Playgroud)

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

任何人都可以解释是否将它们组合在一起会构建查询字符串吗?有人有更好的建议吗?

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

ang*_*son 7

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

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

+是在这种情况下,只是为了把它们连在一起的快捷方式.您可以在如何:在MSDN上组合代理中阅读有关组合代理的更多信息.

这是一个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; }
Run Code Online (Sandbox Code Playgroud)

输出:

X()
Y()
Z()
3
Run Code Online (Sandbox Code Playgroud)

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

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

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; }
Run Code Online (Sandbox Code Playgroud)

简而言之就是不,这不符合你的意愿.


Nic*_*ler 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


Ger*_*old 3

链接谓词是一种创造性的努力,但遗憾的是它不起作用。Lasse 和 Nicholas (+2) 很好地解释了原因。但你也会问:

有人有更好的建议吗?

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

如果您想动态链接表达式,LINQKit 的PredicateBuilder 是执行此操作的绝佳工具。

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());
Run Code Online (Sandbox Code Playgroud)

这就是它的用途Expand

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

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

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

顺便提一句。如果您使用 linq to sql/entities,您也​​可以使用c.lastname == _customer.lastName,因为它会被转换为 SQL,并且数据库排序规则将决定大小写敏感度。