使用include不会改变行为

时间:2016-04-30 23:13:54

标签: c# sql-server performance entity-framework entity-framework-6

有人可以帮助我澄清之间的区别:

 var query = awlt.People.Include(p => p.EmailAddresses)
                    .Where(p => p.LastName.Equals(lastName))
                    .SelectMany(a => a.EmailAddresses)
                    .Select(a => a.EmailAddress1); 
 var query = awlt.People
                    .Where(p => p.LastName.Equals(lastName))
                    .SelectMany(a => a.EmailAddresses)
                    .Select(a => a.EmailAddress1);

在两种情况下我都得到了相同的结果而不知道差异。 Eager Loading是否需要使用Include

4 个答案:

答案 0 :(得分:6)

这两个查询都是使用Eager Loading首先查询相关数据(并且是 Eager loading 是通过使用您猜到的Include方法实现的)默认使用Lazy loading的第二个查询。但由于EmailAddressesSelect()操作,您的查询只会返回SelectMany(),因此Include()方法不会改变行为。要查看示例中的 Include()方法,请阅读以下几行,我将在一个示例中对其进行说明:

要知道这两种加载相关实体之间的某些区别急切加载通常在您需要主表的所有检索行的相关数据时更有效。而当关系 太多时,急切加载将是减少服务器上进一步查询的良好做法。但是当你知道你不会立即需要一个属性时,延迟加载可能是一个不错的选择。而且,如果您的数据库上下文被丢弃并且无法再进行延迟加载,那么急切加载也是一个不错的选择。 要证明一个是Lazy Loading,一个是Eager Loading,请考虑以下代码:

public List<Person> GetEmailAddresses()
{
    using (yourEntities awlt = new yourEntities())
    {
        var query = awlt.People
                .Where(p => p.LastName.Equals(lastName));
        return query.ToList();
    }
}

调用此方法后,您无法延迟加载相关实体,因为已释放数据库。为了证明这一点:

var query = GetEmailAddresses();
foreach (var item in query.SelectMany(a => a.EmailAddresses).Select(a => a.EmailAddress1))
{
    MessageBox.Show(item);                
}

你会收到这个错误:

  

ObjectContext实例已被释放,不能再用于需要连接的操作。

但是如果你改变GetEmailAddresses以使用这样的Eager Loading:

public List<Person> GetEmailAddresses()
{
    using (yourEntities awlt = new yourEntities())
    {
        var query = awlt.People.Include("EmailAddresses")
                .Where(p => p.LastName.Equals(lastName));
        return query.ToList();
    }
}

然后下面的代码应该可以正常工作:

var query = GetEmailAddresses();
foreach (var item in query.SelectMany(a => a.EmailAddresses).Select(a => a.EmailAddress1))
{
    MessageBox.Show(item);                
}

因此,在处理数据库上下文的情况下, Eager Loading 将是更好的选择。

答案 1 :(得分:4)

不了解EF 7,但在EF 6中,这些语句对数据库产生相同的查询,因此基本相同。没有任何延迟加载,没有急切的加载(在某种意义上这个术语通常都是使用的)。

您只需要Include 实现的实体属性。在上面的示例中,您实现了Person.EmailAddresses.EmailAddress1,但仅包含Person.EmailAddresses - 这没有任何效果(有关详细信息,请参阅示例here)。

考虑这个示例代码(细节无关紧要,只有带有代码导航属性的Error实体):

// note we materialized query 
var errors = ctx.Errors.Include(c => c.Code).ToArray();
// no lazy loading happens here - we already loaded all related Codes with Include
var codeIds = errors.Select(c => c.Code.CodeID).ToArray();

这一个:

// no need to include here!
var codeIds = ctx.Errors.Select(c =>c.Code.CodeID).ToArray();

并使用include:

// include has no effect here!
var codeIds = ctx.Errors.Inlcude(c => c.Code).Select(c => c.Code.CodeID).ToArray();

什么是急切加载?当您使用Include语句将其他数据包含到相关实体时。这里的Include语句有 no 效果,它只是什么都没做,所以我们不能说出那个急切的加载。

什么是延迟加载?在您第一次访问导航属性时加载它。您不在示例中执行此操作,因此也没有延迟加载。

这两个示例只是对数据库执行相同的查询(在使用枚举`ToArray`等实现它们之后)。

答案 2 :(得分:1)

两个查询的结果完全相同(也是关于&#39;渴望&#39;懒惰&#39;加载)。
在这种情况下,我认为查询也非常相似或相同,但从不信任EF Provider生成的查询。要查看生成的查询,可以使用断点停止程序并查看查询对象(将鼠标指向它)。这是EF Provider生成的查询。

关于Eager加载(Include语句)在这种情况下它应该没用,因为它用于加载输出对象的属性。在这种情况下,您选择的是EMailAddress1,因此使用Include可以预先加载EMailAddress1的属性(并避免在EMailAddress1访问期间进行延迟查询)。

答案 3 :(得分:0)

如果在运行查询后查看SQL Server Profiler,可以找到差异。因此,在您的第一种情况下,只有一个查询进入您的数据库并从People表和EmailAddresses表中获取记录,而在第二种情况下,它对数据库执行两次查询并首先获取People,然后在第二个查询中获取EmailAddresses。因此,第一种情况称为急切加载,第二种情况称为延迟加载。