IQueryable和懒惰负载

时间:2010-05-07 13:09:54

标签: entity-framework lazy-loading data-layers

我很难确定处理此问题的最佳方法......使用Entity Framework(和L2S),LINQ查询将返回IQueryable。我已经阅读了关于DAL / BLL是否应该返回IQueryable,IEnumerable或IList的各种意见。假设我们使用IList,那么查询会立即运行,并且该控件不会传递到下一层。这使得单元测试等更容易。您无法在更高级别优化查询,但您可以简单地创建另一种方法,允许您优化查询并仍然返回IList。还有更多的利弊。到目前为止一切都很好。

现在实体框架和延迟加载。我在.NET 4 / VS 2010中使用POCO对象和代理。在表示层我做:

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

在这种情况下,GetOrders()返回IList,因此它会在返回PL之前立即执行。但是在下一个foreach中,你有延迟加载,它在获取所有OrderLines时执行多个SQL查询。所以基本上,PL在错误的层中“按需”运行SQL查询。

有没有明智的方法可以避免这种情况?我可以关闭延迟装载,但是有什么能让这个“功能”让每个人都抱怨EF1没有?而且我承认它在很多场景中非常有用。所以我看到了几个选项:

  1. 以某种方式删除实体中的所有关联,并添加方法以返回它们。这违反了默认的EF行为/代码生成,并且使得执行某些复合(多实体)LINQ查询变得更加困难。这似乎是向后退一步。我投票否决。
  2. 如果我们有懒加载,这使得单元测试变得困难,那么就一直走下去并返回IQueryable。你可以在层面上获得更多控制权。我仍然认为这不是一个好选择,因为IQueryable将您与L2S,L2E或您自己的IQueryable完全实现联系在一起。延迟加载可以“按需”运行查询,但不会将您绑定到任何特定接口。我投票否决。
  3. 关闭延迟加载。您必须手动处理关联。这可能是急切加载的.Include()。我在某些特定情况下投赞成票。
  4. 保持IList和延迟加载。在很多情况下我投赞成票,只是因为其他人的麻烦。
  5. 还有其他选择或建议吗?我还没有找到一个真正让我信服的选择。

1 个答案:

答案 0 :(得分:0)

您可以让您的方法接受某种加载策略。

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

HTH