Ami*_*adi 4 c# sql-server entity-framework entity-framework-core
假设您有一个Author域类和一个Book域类,每个类Author可以有 0 个或多个Books(一对多关系)。
如果您执行以下操作:
var dtos = dbContext.Authors.Select(a => new
{
Name = a.Name,
BookNames = a.Books.Select(b => b.Name).ToList()
}).ToList();
Run Code Online (Sandbox Code Playgroud)
您希望生成以下 SQL,因为您只请求了作者姓名和每位作者的书名:
SELECT [a].[Name], [b].[Name]
FROM [Authors] AS [a]
LEFT JOIN [Books] AS [b] ON [a].[Id] = [b].[AuthorId]
ORDER BY [a].[Id], [b].[Id]
Run Code Online (Sandbox Code Playgroud)
但是,实体框架会导致生成以下 SQL:
SELECT [a].[Name], [a].[Id], [b].[Name], [b].[Id]
FROM [Authors] AS [a]
LEFT JOIN [Books] AS [b] ON [a].[Id] = [b].[AuthorId]
ORDER BY [a].[Id], [b].[Id]
Run Code Online (Sandbox Code Playgroud)
这显然是不必要地检索所有作者的 ID 和他们所有书籍的 ID。
这是当您从另一方(即从书籍到作者)进行连接时不会发生这种情况。例如,当您请求书名及其作者姓名时,例如:
var dtos = dbContext.Books.Select(b => new
{
Name = b.Name,
BookNames = b.Author == null ? null : b.Author.Name
}).ToList();
Run Code Online (Sandbox Code Playgroud)
为上述代码生成的 SQL 将是:
SELECT [b].[Name], CASE
WHEN [a].[Id] IS NULL THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END, [a].[Name]
FROM [Books] AS [b]
LEFT JOIN [Authors] AS [a] ON [b].[AuthorId] = [a].[Id]
Run Code Online (Sandbox Code Playgroud)
正如预期的那样,它没有检索 ID。
为什么会这样?为什么 EF Core 会检索 ID,即使我没有将它们包含在我的投影中?
我已经使用 EF Core 3.x + 5.0 RC1 对此进行了测试。如果那很重要。
SQL 查询结果集是扁平的。如果您的查询也是扁平的,那么您所期望的将已生成(并且确实是在您从另一侧执行此操作时生成的),如下所示
var query =
from a in dbContext.Authors
from b in a.Books.DefaultIfEmpty()
select new { Name = a.Name, BookName = b.Name };
Run Code Online (Sandbox Code Playgroud)
但是,您的查询结果形状不同 - 名称 + 相关书名列表。通常 SQL 不能提供这样的形状,因此 EF Core 应该以某种方式转换(分组)返回的平面结果集客户端。记录按两个 id 排序,但正确分组也需要这两个 id。好吧,从技术上讲,除了最后一个以外的所有 id 都是按顺序排列的,所以[b].[Id]在这种情况下是多余的。因此,他们可以将它们按顺序分组(无需像常规的 LINQ to Objects 那样在内存中缓冲整个结果集GroupBy),使用以下内容(在伪代码中):
var query =
from a in dbContext.Authors
from b in a.Books.DefaultIfEmpty()
select new { Name = a.Name, BookName = b.Name };
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
110 次 |
| 最近记录: |