对Groups
的简单查询会为其每个结果生成另一个查询:
using (var db = new DataClasses1DataContext())
{
db.Log = Console.Out;
var query = from g in db.Groups
let stats = from s in g.GroupCountryStats
select new
{
s.CountryId,
s.MembersCount
}
select new
{
g.ObjectId,
g.Name,
Stats = stats.ToArray()
};
var single = query.First(); // Take(2) would result in 2 extra calls, 3 in 3 and so on...
}
控制台输出(Linq-to-Sql 提供程序日志(:
SELECT TOP (1) [t0].[ObjectId] AS [ObjectId], [t0].[Name]
FROM [dbo].[Groups] AS [t0]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.7.3056.0
SELECT [t0].[CountryId] AS [CountryId], [t0].[MembersCount]
FROM [dbo].[GroupCountryStats] AS [t0]
WHERE [t0].[GroupId] = @x1
-- @x1: Input Int (Size = -1; Prec = 0; Scale = 0) [1]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.7.3056.0
Press any key to continue . . .
我想在一个查询中检索所有数据,而不是 N+1 个查询。
我尝试使用DataLoadOptions
,LoadWith<Group>(T=>T.GroupCountryStats)
但结果没有变化。Linq2Sql 仍然对数据库进行 1 次以上的调用。
如何避免这种情况并在一次通话中收到我需要的一切?
这是 LINQ-to-SQL 中的一个错误(或充其量是一个缺点(。没有First
或Take
的查询转换为一个包含OUTER JOIN
的体面SQL查询。但当然,它会给你所有的组。
当 LINQ-to-SQL 遇到限制结果数的语句时,似乎已编程为仅将这些语句应用于根实体,然后分别查询符合实体。
有趣的是:当Skip
和Take
结合在一起时,这种情况不会发生。因此,作为黑客,您可以使用Skip(0).take(x)
,例如:
var results = query.Skip(0).Take(3).ToList();
如果你需要一个结果,你必须做:
var results = query.Skip(0).Take(1).AsEnumerable().First();
如果没有.AsEnumerable()
将再次陷入 1 + 1 查询。(我假设First
的 SQL 翻译在查询形状中优先(。
这是一个肮脏的黑客,对于对抗IQueryable
的泄漏抽象是必要的。确保,如果您选择使用它,请在代码中很好地注释它。
最后的评论:也许你应该考虑转向更高级的ORM,如实体框架。