我有一个班级方法:
public static class ProductExtensions {
public static decimal GetPrice(this Product product, Guid rateId) {
return product.Prices
.Where(p => p.Rate.RateId == rateId)
.Select(b => b.UnitPrice)
.DefaultIfEmpty(product.UnitPrice)
.First();
}
}
并评估表达式
decimal? total =
(from cartItems in storeDB.Carts
where cartItems.CartId == shoppingCartId
select (int?)cartItems.Count * cartItems.Product.GetPrice(store.RateId))
.Sum();
抛出异常:
LINQ to Entities无法识别方法'System.Decimal GetPrice(System.Guid)'方法,并且此方法无法转换为商店表达式。
我在其他地方使用这个相同的代码并且运行得很好:
// Get the price for given rate
decimal price = product.GetPrice(rate.RateId);
知道怎么解决吗?
答案 0 :(得分:11)
试试:
decimal? total =
(from cartItems in storeDB.Carts
where cartItems.CartId == shoppingCartId
select new { cartItems.Count, cartItems.Product})
.AsEnumerable()
.Sum(x => (int?)x.Count * cart.Product.GetPrice(store.RateId));
GetPrice
在SQL中没有等效项,因此您需要在结果上执行它,而不是直接在查询中执行它。 AsEnumerable
迫使Linq从这一点开始将查询视为IEnumerable
(而不是IQueryable
),因此下一步将在内存中执行,而不是在数据库中执行。
答案 1 :(得分:5)
发生异常的原因是实体框架提供程序尝试为扩展方法创建SQL语句。当您单独使用该方法时,它只是为扩展方法的内容创建SQL,它可以正常工作。
我遇到的解决此问题的最佳方法,除了在导致N + 1查询的“外部”查询结果的循环中调用GetPrice
之外,正在使用LinqKit
要使用它,您可以定义表达式树,而不是像这样的扩展方法:
static Expression<Func<Product, Guid, decimal>> priceSelector =
(product, rateId) => product.Prices
.Where(p => p.Rate.RateId == rateId)
.Select(b => b.UnitPrice)
.DefaultIfEmpty(product.UnitPrice)
.First();
请注意,这会创建一个具有相同签名的表达式(除了它不能用作扩展方法)作为您拥有的GetPrice方法。
要将此表达式树与另一个表达式树合并,您需要LinqKit:
decimal? total =
(from cartItems in storeDB.Carts
where cartItems.CartId == shoppingCartId
select (int?)cartItems.Count *
priceSelector.Invoke(cartItems.Product, store.RateId))
.Expand()
.Sum();
.Invoke()
调用会向表达式树添加一个调用。 Expand()
调用内联此方法,因此您可以获得一个可以转换为SQL的大型表达式树。
此方法将编写一个类似下面的查询,但具有可重复使用的priceSelector
:
decimal ? total =
(from cartItems in storeDB.Carts
where cartItems.CartId == shoppingCartId
select (int?)cartItems.Count * product.Prices
.Where(p => p.Rate.RateId == rateId)
.Select(b => b.UnitPrice)
.DefaultIfEmpty(product.UnitPrice)
.First()).Sum();