Метод GetPrice () не может быть переведен в выражение магазина - PullRequest
6 голосов
/ 31 июля 2011

У меня есть метод класса:

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);

Есть идеи, как это решить?

Ответы [ 2 ]

11 голосов
/ 31 июля 2011

Попробуйте:

    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) с этой точки, поэтому то, что происходит дальше, выполняется в памяти, а не в БД.

5 голосов
/ 31 июля 2011

Причина исключения заключается в том, что поставщик Entity Framework пытается создать операторы 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();

Обратите внимание, что при этом создается выражение с той же сигнатурой (за исключением того, что его нельзя использовать в качестве метода расширения), как у используемого вами метода 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(); 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...