LINQ to Entities和Lazy Loading

时间:2008-12-03 22:27:37

标签: linq-to-sql linq-to-entities

今天在controversial blog post中,Hackification对新的LINQ To Entities框架中的错误进行了质疑:

  

假设我搜索客户:

var alice = data.Customers.First( c => c.Name == "Alice" );
     

很好,这很好用。现在让我们看看   如果我能找到她的一个命令:

 var order = ( from o in alice.Orders
          where o.Item == "Item_Name"
          select o ).FirstOrDefault();
     

LINQ-to-SQL将找到子行。   LINQ-to-Entities将默默返回   什么都没有。

     

现在我假设我迭代了   数据库中的所有订单:

foreach( var order in data.Orders ) { 
Console.WriteLine( "Order: " + order.Item ); }
     

现在重复我的搜索:

var order = ( from o in alice.Orders
          where o.Item == "Item_Name"
          select o ).FirstOrDefault();
     

哇! LINQ-to-Entities突然之间   告诉我子对象存在,   尽管早些时候告诉我它   没有!

我最初的反应是,这必须是一个错误,但经过进一步的考虑(和backed up by the ADO.NET Team),我意识到这种行为是由于实体框架在延迟加载Orders子查询时引起的。 datacontext。

这是因为order是LINQ-To-Object查询:

var order = ( from o in alice.Orders
      where o.Item == "Item_Name"
      select o ).FirstOrDefault();

并没有以任何方式访问datacontext,而他的foreach循环:

 foreach( var order in data.Orders )

正在访问datacontext。

LINQ-To-SQL实际上为Orders创建了延迟加载的属性,因此当访问它们时,会执行另一个查询,LINQ to Entities会让您自行检索相关数据。

现在,我不是ORM的忠实粉丝,这正是原因所在。我发现为了让你想要的所有数据都在你的指尖,他们会反复执行你背后的查询,例如,上面的linq-to-sql查询可能会为每行客户运行一个额外的查询来获取订单

然而,EF没有这样做似乎主要违反了最不惊讶的原则。虽然这是一种技术上正确的做事方式(您应该运行第二个查询来检索订单,或从视图中检索所有内容),但它的行为与您对ORM的预期不同。

那么,这是一个很好的框架设计吗?或者微软是不是在为我们考虑这个?

6 个答案:

答案 0 :(得分:12)

乔,

我也和实体一起玩过linq。在赶上linq to SQL之前还有很长的路要走。我必须使用linq实现每个类型继承表的实体。我最近发现了一篇很好的文章,解释了整个公司2个不同的ORM技术here

但是,你可以通过以下方式进行延迟加载:

// Lazy Load Orders 
var alice2 = data.Customers.First(c => c.Name == "Alice");

// Should Load the Orders
if (!alice2.Orders.IsLoaded)
    alice2.Orders.Load();

或者您可以在原始查询中包含订单:

// Include Orders in original query
var alice = data.Customers.Include("Orders").First(c => c.Name == "Alice");

// Should already be loaded
if (!alice.Orders.IsLoaded)
    alice.Orders.Load();

希望它有所帮助。

戴夫

答案 1 :(得分:5)

  

那么,这是一个很好的框架设计吗?或者微软是否为我们考虑过这个?

好吧让我们分析一下 - 微软所做的所有想法,我们不必真正让我们变得懒散的程序员。但总的来说,它确实使我们更有成效(大多数情况下)。那么 他们 over 在想什么?或者他们只是为我们思考

答案 2 :(得分:2)

如果LINQ-to-Sql和LINQ-to-Entities来自两家不同的公司,那将是一个可接受的差异 - 没有法律规定所有LINQ-To-Whatevers必须以相同的方式实现。

然而,他们都来自微软 - 而我们不应该需要对他们内部开发团队和流程的深入了解,以了解如何使用两个不同的东西,在他们的脸上,看起来完全一样

ORM有自己的位置,确实填补了试图完成工作的人的空白,但ORM使用必须确切知道他们的ORM如何完成工作 - 将其视为难以穿透的黑盒子只会让你陷入困境。

答案 3 :(得分:2)

对这个问题已经失去了几天,我表示同情。

如果存在“错误”,那就是有一种合理的倾向,即期望抽象层能够隔离这些问题。从LINQ到实体,再到数据库层,加倍。

例如,必须从MS-SQL(使用LingToSQL)切换到MySQL(使用LinqToEntities),至少可以认为LINQ是相同的,如果不仅仅是为了节省必须重新使用的成本 - 编写程序逻辑。

不得不使用.Load()和/或LINQ与.Include()乱码,因为引擎盖下的持久性机制似乎有点令人不安,尤其是在无声失败的情况下。 LINQ层至少应该表现得一致。

许多ORM框架使用代理对象透明地动态加载惰性对象,而不是只返回null,尽管我对未加载集合的异常感到满意。

我倾向于不买入他们故意为了你的利益而做的借口;其他ORM框架允许您根据需要注释是否需要预先加载或延迟加载。这可以在这里完成。

答案 4 :(得分:1)

我对ORM知之甚少,但作为LinqToSql和LinqToEntities的用户,我希望当你尝试查询Orders的Orders时,它会在你进行linq查询时为你做额外的查询(而不是查询任何内容或查询每一行的所有内容。)

看起来很自然

from o in alice.Orders where o.Item == "Item_Name" select o

开始工作,因为这是人们首先使用ORM的原因之一(为了简化数据访问)。

我对LinqToEntities的了解越多,我认为LinqToSql就能满足大多数开发人员的需求。我通常只需要一对一的表格映射。

答案 5 :(得分:1)

即使你不必了解微软的内部开发团队和流程,事实上这两种技术是两种完全不同的野兽。

为简单起见,LINQ to SQL的设计决策是隐式延迟加载集合。 ADO.NET实体框架团队没有想要在用户不知情的情况下执行查询,因此他们设计了为第一个版本显式加载的API。

LINQ to SQL已经移交给ADO.NET团队,因此您可能会在未来看到API的整合,或者LINQ to SQL被纳入实体框架,或者您可能会看到LINQ to SQL因忽视而萎缩最终被弃用了。