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