Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/324.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么EF在执行一对多连接时不必要地检索ID?_C#_Sql Server_Entity Framework_Entity Framework Core - Fatal编程技术网

C# 为什么EF在执行一对多连接时不必要地检索ID?

C# 为什么EF在执行一对多连接时不必要地检索ID?,c#,sql-server,entity-framework,entity-framework-core,C#,Sql Server,Entity Framework,Entity Framework Core,假设您有一个作者域类和一个书籍域类,每个作者可以有0个或多个书籍(一对多关系) 如果您执行以下操作: var dtos = dbContext.Authors.Select(a => new { Name = a.Name, BookNames = a.Books.Select(b => b.Name).ToList() }).ToList(); 您希望生成以下SQL,因为您只请求了作者的姓名以及每个作者的书籍名称: SELECT [a].[Name], [b].

假设您有一个
作者
域类和一个
书籍
域类,每个
作者
可以有0个或多个
书籍
(一对多关系)

如果您执行以下操作:

var dtos = dbContext.Authors.Select(a => new
{
    Name = a.Name,
    BookNames = a.Books.Select(b => b.Name).ToList() 
}).ToList();
您希望生成以下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]
但是,实体框架会生成以下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]
这显然是不必要的检索所有作者的ID和他们所有书籍的ID

这是当你从另一边,也就是从书到作者,加入时,这不会发生。例如,当您请求书名及其作者姓名时,如:

var dtos = dbContext.Books.Select(b => new
{
    Name = b.Name,
    BookNames = b.Author == null ? null : b.Author.Name
}).ToList();
为上述代码生成的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]
未按预期检索ID

为什么会这样?为什么EF Core会在我的投影中没有包含ID的情况下检索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 };
但是,您的查询结果形状不同-名称+相关图书名称列表。通常SQL不能提供这样的形状,因此EF Core应该以某种方式转换(组)返回的平面结果集客户端。记录按两个ID排序,但正确分组也需要这两个ID。从技术上讲,除了顺序中的最后一个Id之外,所有Id都是多余的,因此在本例中,
[b].[Id]
。因此,他们可以按顺序将它们分组(无需在内存中缓冲整个结果集,如常规LINQ to Objects
GroupBy
),如下所示(在伪代码中):

var enumerator=query.GetEnumerator();
bool hasNext=enumerator.MoveNext();
while(hasNext)
{

var aId=枚举数。当前[“aId”]/Ivan Stoev解释了第一个查询返回ID的原因。第二个查询非常不同。第一个查询为每个作者返回一个根对象,其中包含所有作者的书籍,而第二个查询返回一个简单的对象列表。在这种情况下,不需要知道ID。从
作者开始的等效项是

var bookNames= from a in dbContext.Authors
               from b in a.Books
               select new {a.Name,BookNames=new {b.Name}};

猜一猜,反对/改变tracking@Diado没有要跟踪的对象,因为未选择实体,而是一个只包含名称的匿名对象。如果您仔细想想,您不是在要求平面作者、书名对。您是在要求多本书的一个作者名称。如果没有PK v,EF无法生成该层次结构值。没有什么可以阻止多个作者拥有相同的名称,您如何知道哪本书属于哪位作者?第二个查询非常不同,不需要ID。每本书只有一个可能的AuthorName。即使如此,仍会检查
author.ID
,以确保joinOk生成任何可能的
NULL
s,明白了。谢谢。所以使用
[a].[Id]
是合理的。但是正如你自己提到的,
[b].[Id]
是多余的,那么为什么EF Core在这种情况下检索它呢?@Arad Bug/defect/missing optimization。我们都会做这样的事情,尤其是在有截止日期和不同优先级的情况下。