为什么我的Entity Framework查询会返回两次记录?

时间:2011-08-25 18:23:53

标签: entity-framework

我有两个表,一个是联系人(人),另一个是地址。 Gregory Alderson有一个Contact条目和两个Address条目。

这是代码:

Code

为Gregory Alderson返回两条记录:

Results 1

如果我将LazyLoadingEnable设置为'true',它会做同样的事情,但两个记录都包含两个地址:

Results 2

我正在学习的这本书(编程实体框架第2版 - 好书BTW)解释说LazyLoading已被禁用,因此Count方法不会影响结果,但到目前为止还没有解释为什么会这样做。

有人可以向我解释为什么我关闭了LazyLoading的两条记录,并且两条带有LazyLoading的记录(都有两个地址)都已启用?

3 个答案:

答案 0 :(得分:2)

更好地了解正在发生的事情的好方法是运行查询分析器并查看针对数据库执行的SQL语句或更好地获取Ayende's EF Profiler的副本。

基本上,通过急切加载,您需要更明确地了解要返回的相关实体。这是使用上下文对象上的Include方法完成的。如果没有启用延迟加载,您只需对数据库进行一次命中,然后仅针对本地保存的数据进行评估,而不是再向数据库发出请求以获取Count()中使用的其他数据。

答案 1 :(得分:1)

这里的问题似乎是由于你所选择的。具体做法是:

select new {a, a.Contact}

在这种情况下,联系人实际上是a的导航属性。当您选择a时,您将选择a上的所有内容,包括联系人。同时选择a.Contact意味着您可以联系两次。

启用延迟加载后,您无需选择它。如果您选择a然后只需在代码中的其他位置使用a.Contact,EF就会为您加载它。延迟加载中的“懒惰”是它没有加载,除非你真的尝试使用它。延迟加载,你需要这个:

select a

关闭延迟加载,这不会发生。但你仍然不想选择它。相反,你可以使用Include:

from a in context.Addresses.Include("Contact") select a

告诉EF您始终希望它加载Contact导航属性并立即执行此操作。它将立即可用,如果您处理上下文(延迟加载不是这种情况),它仍然可用。

我怀疑这里的问题是通过选择a的所有AND属性,你会得到一个奇怪的副作用。

答案 2 :(得分:0)

OP的问题,总结在最后两段,是:

  

我正在学习的这本书(编程实体框架第2版 - 好书BTW)解释说LazyLoading已被禁用,因此Count方法不会影响结果,但到目前为止还没有解释为什么会这样做。

     

有人可以向我解释为什么我关闭了LazyLoading的两条记录,并且两条带有LazyLoading的记录(都有两个地址)都已启用?

Daz解释了关于LazyLoading对Count()的影响的部分。但Daz和Tridus都没有解释为什么OP无论LazyLoading如何在输出中获得Gregory Anderson的两个Contact记录。这是我将在这里回答的问题。

问题在于迭代基本上发生在Addresses之上。也就是说,外部foreach循环对加拿大的每个Address执行一次。因为Gregory Anderson在加拿大有两个地址,所以循环执行两次。

请注意,如果Gregory Anderson在美国也有一个地址,那么循环仍然会执行两次,但是所有三个地址都会被打印出来,而不仅仅是加拿大的地址。

如果意图实际上是列出每个Contact一次,然后列出Address的每个Contact,则更合适的设计如下:

var contacts = context.Contacts
    .Where(c => c.Addresses.Any(a => a.CountryRegion == "Canada"));
foreach (var c in contacts)
{
    Console.WriteLine("{0} {1}, Addresses: {2}",
        c.FirstName.Trim(),
        c.LastName.Trim(),
        c.Addresses.Count());
    foreach (var a in c.Addresses)
    {
        Console.WriteLine("...{0}, {1}\n", a.Street1.Trim(), a.City.Trim());
    }
}

(我尽量保持代码尽可能接近相同,但我想不出如何使用查询语法编写查询,所以我使用LINQ语法因为我对它更熟悉.. 。)

此代码将生成从数据库返回的Contacts的不同列表,然后每个Contact将与每个子Addresses一起输出。

希望能帮助那些可能正在处理此事并且没有找到其他答案的人在这方面有所帮助。