IQueryable и ленивая загрузка - PullRequest
0 голосов
/ 07 мая 2010

Мне трудно определить лучший способ справиться с этим ... С помощью Entity Framework (и L2S) запросы LINQ возвращают IQueryable. Я читал различные мнения о том, должен ли DAL / BLL возвращать IQueryable, IEnumerable или IList. Предполагая, что мы используем IList, запрос выполняется немедленно, и этот контроль не передается следующему уровню. Это облегчает юнит-тестирование и т. Д. Вы теряете возможность уточнять запрос на более высоких уровнях, но вы можете просто создать другой метод, который позволит вам уточнить запрос и при этом вернуть IList. И есть еще много плюсов / минусов. Пока все хорошо.

Теперь идет Entity Framework и ленивая загрузка. Я использую объекты POCO с прокси в .NET 4 / VS 2010. На уровне представления я делаю:

foreach (Order order in bll.GetOrders())
{
  foreach (OrderLine orderLine in order.OrderLines)
  {
    // Do something
  }
}

В этом случае GetOrders () возвращает IList, поэтому он выполняется непосредственно перед возвратом в PL. Но в следующем foreach у вас ленивая загрузка, которая выполняет несколько SQL-запросов по мере получения всех строк OrderLines. Таким образом, в основном, PL выполняет запросы SQL «по требованию» на неправильном уровне.

Есть ли разумный способ избежать этого? Я мог бы отключить ленивую загрузку, но тогда какой смысл иметь эту «особенность», которой все жаловались, что EF1 не имел? И я признаю, что это очень полезно во многих сценариях. Итак, я вижу несколько вариантов:

  1. Каким-то образом удалите все ассоциации в сущностях и добавьте методы для их возврата. Это идет вразрез со стандартным поведением EF / генерацией кода и усложняет выполнение некоторых составных запросов (из нескольких сущностей) LINQ. Это похоже на шаг назад. Я голосую нет.
  2. Если в любом случае у нас отложенная загрузка, что затрудняет юнит-тестирование, то проделайте весь путь и верните IQueryable. Вы будете иметь больше контроля над слоями. Я все еще не думаю, что это хороший вариант, потому что IQueryable связывает вас с L2S, L2E или с вашей собственной полной реализацией IQueryable. Ленивая загрузка может запускать запросы «по требованию», но не привязывает вас к какому-либо конкретному интерфейсу. Я голосую нет.
  3. Отключить ленивую загрузку. Вам придется обрабатывать ваши ассоциации вручную. Это может быть с нетерпением загрузки .Include (). Я голосую "за" в некоторых конкретных случаях.
  4. Сохраняйте IList и ленивую загрузку. Я голосую "за" во многих случаях, только из-за проблем с другими.

Есть ли другие варианты или предложения? Я не нашел вариант, который действительно убеждает меня.

1 Ответ

0 голосов
/ 08 мая 2010

Вы можете заставить свои методы принимать какую-то стратегию загрузки.

Func<ObjectSet<Order>, ObjectQuery<Order>> loadSpan = 
orders=> orders.Include("OrderLines");

foreach (Order order in bll.GetOrders(loadSpan)) 
{ 
  foreach (OrderLine orderLine in order.OrderLines) 
  { 
    // Do something 
  } 
}

А внутри вашего метода GetOrders вы делаете что-то вроде

public IList<Oorder> GetOrders(
                     Func<ObjectSet<Order>, ObjectQuery<Order>> loadSpan)
{ 
    var ordersWithSpan = loadSpan(context.OrderSet);
    var orders = from order in ordersWithSpan
                 where ...your normal filters etc

    return orders.ToList();
}

Это позволит вам указать графики полной загрузки для каждого варианта использования. Конечно, вы можете также обернуть эти стратегии в некоторый класс-обертку, чтобы вы написали:

//wrapped in a static class "OrderLoadSpans"
foreach (Order order in bll.GetOrders(OrderLoadSpans.WithOrderLines))

НТН

...