我已经看到可以将编译的方法一起添加。
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表达式,但我对你可能从编译方法中获得的内容感兴趣并添加它们!
答案 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
和
答案 2 :(得分:3)
将谓词链接起来是一种创造性的努力,太糟糕了,它不起作用。 Lasse和Nicholas(+2)非常好地解释了原因。但你也问:
有人有更好的建议吗?
编译表达式的缺点是它们不再是表达式,因此不能与SQL查询提供程序支持的IQueryable
一起使用(例如linq to sql或linq to entities)。我认为_repo.Customers
是IQueryable
。
如果您想动态链接表达式,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,数据库整理将决定区分大小写。