未包含的导航属性和数据库往返

时间:2014-09-19 16:20:28

标签: sql-server entity-framework

假设我有这句话:

var eggs = db.Nests.Single(b => b.id = 20).Birds.FirstOrDefault().Eggs;

在我的探查器跟踪中,我看到有多个命令正在执行:

SQL:BatchString
SELECT TOP (2) 
[Extent1].[id]
...
FROM [dbo].[Nest] AS [Extent1]
WHERE 20 = [Extent1].[id]
SQL:BatchCompleted

RPC:Completed
exec sp_executesql N'SELECT 
[Extent1].[Id] AS [Id], 
...
FROM [dbo].[Bird] AS [Extent1]
WHERE [Extent1].[NesId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=20

RPC:Completed
exec sp_executesql N'SELECT 
[Extent1].[Id] AS [Id], 
...
FROM [dbo].[Egg] AS [Extent1]
WHERE [Extent1].[BirdId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=13

是否所有这些命令都在同一个请求中执行,或者每个命令都是往返的?

此外,我是否可以确认这是编写上述内容的最有效方式:

var eggs = db.Nests.Include("Birds")
           .Single(b => b.id = 20)
           .Birds.Include("Eggs")
           .FirstOrDefault()
           .Eggs;

或者明确的加入会更好吗?

2 个答案:

答案 0 :(得分:2)

首先,在第一个示例中获得多个查询是完全正常的。除非您在第二个示例中使用Include(),否则访问任何导航属性都会生成新的SQL查询。

我不确定您的数据库架构是如何布局的,但假设Birds有一个名为Nests Id_Nest的外键,您可以将此查询重写为:

var eggs = db.Birds.Include("Eggs")
                   .First(x => x.Id_Nest == 20)
                   .Eggs
                   .ToList();

Include()生成的代码与显式join的代码相同,因此无需担心代码不同。至于往返,如果你的意思是网络服务器和数据库之间的往返,那么是的,每个查询可能有一个往返。如果连接保持打开,这并不是一件大事。但最好是通过joinInclude()一次性获取所需的一切,而不是将数据库击中3次。

答案 1 :(得分:1)

您在问题中描述的行为是lazy-loading功能的典型示例,该功能有助于快速构建工作的代码,但执行效果不佳因为远非最佳。作为旁注,我个人总是建议完全停用延迟加载。

  

是否所有这些命令都在同一个请求中执行,或者是   每个都往返一次吗?

是的,每次请求都有一次往返。在你的例子中,这可能不是什么大问题,但想象你的代码在一个循环中......你将你拥有的请求数(3)乘以迭代次数。你最终可能会收到成千上万的请求。

  

此外,我能否确认这是最有效的   写上面的方法:

这不是编写查询的最有效方法。实际上它可能甚至不会编译(你不能像你那样使用Include)。

作为一般性建议,最有效的方法是包含您需要的所有导航属性,然后,只有这样,才能使用单个ToList / {{来执行查询 1}} / Single或实际执行查询并实现实体的任何函数。所以你只有一个SQL请求。

对于您提供的具体示例,@ Radu Porumb建议可能是生成最简单/最有效的SQL的查询。当然,您必须使用分析器验证此假设。