今天在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的预期不同。
那么,这是一个很好的框架设计吗?或者微软是不是在为我们考虑这个?
答案 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因忽视而萎缩最终被弃用了。