9

在今天的一篇有争议的博客文章中,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 突然告诉我子对象存在,尽管之前告诉我它不存在!

我最初的反应是这一定是一个错误,但经过进一步考虑(并得到 ADO.NET 团队的支持),我意识到这种行为是由实体框架引起的,而不是在 Alice 被拉出时延迟加载 Orders 子查询数据上下文。

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

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

并且没有以任何方式访问数据上下文,而他的 foreach 循环:

 foreach( var order in data.Orders )

正在访问数据上下文。

LINQ-To-SQL 实际上为 Orders 创建了延迟加载的属性,以便在访问时执行另一个查询,LINQ to Entities 让您手动检索相关数据。

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

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

那么,这是好的框架设计吗?还是微软在为我们考虑这个问题?

4

6 回答 6

12

乔恩,

我也一直在玩 linq to entity。在赶上 linq to SQL 之前还有很长的路要走。对于每个类型继承的表,我不得不使用 linq 到实体。我最近发现了一篇很好的文章,它在这里解释了整个 1 公司 2 种不同的 ORM技术

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

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

希望能帮助到你。

戴夫

于 2008-12-18T15:39:16.203 回答
5

那么,这是好的框架设计吗?还是微软在为我们考虑这个问题?

好吧,让我们分析一下——微软所做的所有想法,我们不必真的让我们成为更懒惰的程序员。但总的来说,它确实让我们更有效率(在大多数情况下)。那么他们是想太多,还是只是在为我们考虑

于 2008-12-03T22:48:52.083 回答
2

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

然而,他们都来自微软——我们不需要对他们的内部开发团队和流程有深入的了解,就可以知道如何使用表面上看起来完全一样的两种不同的东西

ORM 有自己的位置,并且确实填补了人们试图完成工作的空白,但是 ORM 的使用者必须确切地知道他们的 ORM 是如何完成工作的——把它当作一个不可穿透的黑匣子只会给你带来麻烦。

于 2008-12-03T22:43:28.490 回答
2

在这个问题上浪费了几天时间,我很同情。

“错误”(如果有的话)是有一种合理的倾向,即期望抽象层将与这类问题隔离开来。从 LINQ 到实体,再到数据库层,更是如此。

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

仅仅因为引擎盖下的持久性机制发生了变化,就不得不用 .Load() 和/或 LINQ 用 .Include() 乱扔代码似乎有点令人不安,尤其是在静默失败的情况下。LINQ 层至少应该表现一致。

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

我倾向于不接受他们故意为您的利益而做的借口;其他 ORM 框架让您可以根据需要注释您是想要预先加载还是延迟加载。在这里也可以这样做。

于 2010-02-26T17:17:23.220 回答
1

我不太了解 ORM,但作为 LinqToSql 和 LinqToEntities 的用户,我希望当您尝试查询 Alice 的订单时,它会在您进行 linq 查询时为您执行额外的查询(而不是不查询任何内容或查询每一行的所有内容)。

期待似乎很自然

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

考虑到这是人们首先使用 ORM 的原因之一(以简化数据访问)。

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

于 2008-12-03T22:37:30.780 回答
1

尽管您不必了解 Microsoft 的内部开发团队和流程,但事实是这两种技术是两种完全不同的野兽。

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

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

于 2008-12-09T22:01:29.630 回答