IQueryable 和延迟加载

发布于 2024-09-01 09:42:55 字数 1133 浏览 3 评论 0原文

我很难确定处理此问题的最佳方法...使用实体框架(和 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 中,您会进行延迟加载,它会在获取所有 OrderLine 时执行多个 SQL 查询。所以基本上,PL 在错误的层中“按需”运行 SQL 查询。

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

  1. 以某种方式删除实体中的所有关联并添加方法来返回它们。这违背了默认的 EF 行为/代码生成,并且使得执行某些复合(多实体)LINQ 查询变得更加困难。这似乎是一种倒退。我投反对票。
  2. 如果我们有延迟加载,这使得单元测试变得困难,那么就一直返回 IQueryable。您将在更高层拥有更多控制权。我仍然不认为这是一个好的选择,因为 IQueryable 将您与 L2S、L2E 或您自己的 IQueryable 完整实现联系在一起。延迟加载可以“按需”运行查询,但不会将您绑定到任何特定接口。我投反对票。
  3. 关闭延迟加载。您必须手动处理您的关联。这可能是通过急切加载的 .Include() 实现的。在某些特定情况下我投赞成票。
  4. 保留 IList 和延迟加载。在很多情况下我都会投赞成票,只是因为与其他人有麻烦。

还有其他选择或建议吗?我还没有找到真正让我信服的选项。

I'm having a hard time determining the best way to handle this... With Entity Framework (and L2S), LINQ queries return IQueryable. I have read various opinions on whether the DAL/BLL should return IQueryable, IEnumerable or IList. Assuming we go with IList, then the query is run immediately and that control is not passed on to the next layer. This makes it easier to unit test, etc. You lose the ability to refine the query at higher levels, but you could simply create another method that allows you to refine the query and still return IList. And there are many more pros/cons. So far so good.

Now comes Entity Framework and lazy loading. I am using POCO objects with proxies in .NET 4/VS 2010. In the presentation layer I do:

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

In this case, GetOrders() returns IList so it executes immediately before returning to the PL. But in the next foreach, you have lazy loading which executes multiple SQL queries as it gets all the OrderLines. So basically, the PL is running SQL queries "on demand" in the wrong layer.

Is there any sensible way to avoid this? I could turn lazy loading off, but then what's the point of having this "feature" that everyone was complaining EF1 didn't have? And I'll admit it is very useful in many scenarios. So I see several options:

  1. Somehow remove all associations in the entities and add methods to return them. This goes against the default EF behavior/code generation and makes it harder to do some composite (multiple entity) LINQ queries. It seems like a step backwards. I vote no.
  2. If we have lazy loading anyway which makes it hard to unit test, then go all the way and return IQueryable. You'll have more control farther up the layers. I still don't think this is a good option because IQueryable ties you to L2S, L2E, or your own full implementation of IQueryable. Lazy loading may run queries "on demand", but doesn't tie you to any specific interface. I vote no.
  3. Turn off lazy loading. You'll have to handle your associations manually. This could be with eager loading's .Include(). I vote yes in some specific cases.
  4. Keep IList and lazy loading. I vote yes in many cases, only due to the troubles with the others.

Any other options or suggestions? I haven't found an option that really convinces me.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

往事随风而去 2024-09-08 09:42:55

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

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

You could make your methods accept some sort of load strategy.

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

And inside your GetOrders method, you do something like

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

This will allow you to specify entire load graphs per use case.
You can ofcourse also wrap those strategies up in some wrapper class so you would write:

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

HTH

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文