方法GetPrice()无法转换为商店表达式

Mar*_*arc 6 .net c# linq linq-to-entities entity-framework

我有一个类方法:

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

并评估表达

        decimal? total = 
            (from cartItems in storeDB.Carts
            where cartItems.CartId == shoppingCartId
            select (int?)cartItems.Count * cartItems.Product.GetPrice(store.RateId))
            .Sum();
Run Code Online (Sandbox Code Playgroud)

抛出异常:

LINQ to Entities无法识别方法'System.Decimal GetPrice(System.Guid)'方法,并且此方法无法转换为商店表达式.

我在其他地方使用这个相同的代码,工作得很好:

        // Get the price for given rate
        decimal price = product.GetPrice(rate.RateId);
Run Code Online (Sandbox Code Playgroud)

知道怎么解决吗?

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

GetPrice在SQL中没有等效项,因此您需要在结果上执行它,而不是直接在查询中执行它.AsEnumerable迫使Linq 从这一点开始将查询视为IEnumerable(而不是那个IQueryable),因此下一步将在内存中执行,而不是在DB中执行.


San*_*ken 5

发生异常的原因是实体框架提供程序尝试为扩展方法创建SQL语句.当您单独使用该方法时,它只是为扩展方法的内容创建SQL,这很好.

除了GetPrice在"外部"查询的结果中调用导致N + 1查询的循环之外,我遇到的解决此问题的最佳方法是使用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();
Run Code Online (Sandbox Code Playgroud)

请注意,这会创建一个具有相同签名的表达式(除了它不能用作扩展方法),就像您拥有的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();
Run Code Online (Sandbox Code Playgroud)

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