假设我有这行:
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;
还是明确加入会更好?
首先,在第一个示例中获得多个查询是非常正常的。除非像第二个示例中那样使用Include()
,否则访问任何导航属性都会生成一个新的SQL查询。
我不确定你的数据库模式是如何布局的,但假设Birds
有一个名为Id_Nest
的Nests
外键,你可以将这个查询重写为:
var eggs = db.Birds.Include("Eggs")
.First(x => x.Id_Nest == 20)
.Eggs
.ToList();
Include()
生成与显式join
大致相同的代码,因此无需担心代码不同。至于往返,如果你指的是web服务器和数据库之间的往返,那么是的,每个查询可能都有一个往返。不过,如果连接保持开放,那也没什么大不了的。但最好是通过join
或Include()
一次性获得所需的一切,而不是三次点击数据库。
您在问题中描述的行为是延迟加载功能的一个典型示例,该功能有助于快速构建工作但执行较差的代码,因为性能远未达到最佳。顺便说一句,我个人总是建议完全停用懒惰加载。
所有这些命令是在同一个请求中执行的,还是每个都有往返行程吗?
是的,每个请求有一个往返行程。在您的示例中,这可能不是什么大不了的事情,但想象一下您的代码在一个循环中。。。您将拥有的请求数(3)乘以迭代次数。你最终可能会收到成千上万的请求。
此外,我能确认这是最有效的吗写上面的方法:
这不是编写查询的最有效方法。实际上,它可能甚至不会编译(您不能像以前那样使用Include
)。
作为一般建议,最有效的方法是包括您需要的所有导航属性,然后,并且只有在那时,使用单个ToList
/Single
/First
或实际执行查询并具体化实体的任何函数来执行查询。所以您只有一个SQL请求。
对于您给出的具体示例,@Radu Porumb建议可能是生成最简单/最高效SQL的查询。当然,您必须使用探查器来验证这个假设。