我在LINQ to Entities中有以下查询:
var query = from p in db.Products
where p.Price > 10M
select p;
此时查询尚未执行,我想编写一个根据某些条件返回true / false的查询:
return query.Any(p => p.IsInStock &&
(p.Category == "Beverage" ||
p.Category == "Other"));
这很好用;但是,我想从我的代码中重用一些东西。我有很多方法需要根据类别是饮料还是其他来过滤,所以我尝试创建一个委托:
Func<Product, bool> eligibleForDiscount = (product) => product.Category == "Beverage" || product.Category == "Other";
我想用委托代替内联检查:
return query.Any(p => p.IsInStock && eligibleForDiscount(p));
这给我一个错误,说LINQ to Entities不支持Invoke。为什么我不能将内联代码替换为这样的委托,有什么方法可以通过其他方式实现我的重用?
答案 0 :(得分:6)
回想一下,引擎Linq-to-{DATABASE}
只是将您生成的IQueryable
转换为Sql。
您无法内联这样的代码,因为Invoke
(您在调用Func
或Action
时实际调用的方法)没有一致的方法将其转换为一个sql语句(你可以在那里做任何)。
那说你可以通过拆分来重复使用部件:
var query = from p in db.Products
where p.Price > 10M
select p;
query = query.Where(p => p.IsInStock);
query = query.Where(p => p.Category == "Beverage" || p.Category == "Other");
return query.Any();
这些都可以放入采用IQueryable<Product>
并返回相同(但已过滤)的方法中。然后你可以重复使用你的内容!
答案 1 :(得分:2)
问题是IQueryable
需要生成一个SQL表达式来传递给RDBMS,当它只有一个不透明的谓词时它就不能这样做。
显而易见但效率低下的方法是按如下方式重写查询:
return query.Where(p => p.IsInStock).AsEnumerable().Any(eligibleForDiscount);
一种不那么简单的方法如下:
bool GotEligible(Expression<Func<Product,bool>> pred) {
return query.Where(p => p.IsInStock).Any(pred);
}
请注意,此方法不是谓词,而是采用谓词表达式。现在它对EF是透明的,并且可以毫无问题地转换为SQL查询。
答案 2 :(得分:0)
只要您坚持使用IQueryable,就可以在函数中保留可重用的查询。
public IQueryable<Product> EligibleForDiscount(IQueryable<Product> products)
{
return products.Where(p => product.Category == "Beverage" ||
product.Category == "Other");
}
现在像任何其他函数一样调用它:
IQueryable<Product> query = (from p in db.Products
where p.Price > 10M
select p);
query = EligibleForDiscount(query);