使用C#EF Core返回包含null子节点的父节点。包括

时间:2018-01-19 19:42:23

标签: c# entity-framework entity-framework-core

我们的目标是使用Entity Framework Core和.Include(...)扩展方法查询数据库,以返回具有子项的对象集合,其中一些子项将为null。

我们有一个带有C#model Project 的表 Projects 和一个带有C#model Location 的表 Locations

每个 Project 都有一个或零个 Location 对象,对象如下所示:

public class Project
{
     public string LocationId { get; set; }
     public Location Location { get; set; }
}

public class Location
{
     public string LocationId { get; set; }
}

数据库设置如下所示:

        modelBuilder.Entity<Location>(entity =>
        {
            entity.HasKey(location => location.LocationId);
            entity.ToTable("Locations");
        });
        modelBuilder.Entity<Project>(entity =>
        {                
            entity.HasOne(project => project.Location).WithMany()
            .HasForeignKey(x => x.LocationId);                
            entity.ToTable("Projects");

        });

我们创建的查询是:

var projects = dbContext.Projects.Include(x => x.Location);

当我们期待 LEFT OUTER JOIN 时,EF生成的SQL包含 LEFT JOIN

SELECT [project].[LocationId] 
FROM [Projects] AS [project] 
LEFT JOIN [Locations] AS [c] ON [project].[LocationId] = [c].[LocationId]

结果是只返回带有位置的项目。我们想要所有项目及其位置。

this link我知道.Include(...)根据外键的可为空性决定执行LEFT JOIN或LEFT OUTER JOIN:

  

Code First根据外键的可为空性推断出关系的多样性。如果属性可以为空,那么关系将被注册为可选。

由于事情并非如此,因此缺少一些东西。

无论是否填充其位置,都需要进行哪些修改才能返回所有项目?

谢谢!

4 个答案:

答案 0 :(得分:2)

您可以在DefaultIfEmpty()之后使用Include()方法。

例如:

var projects = dbContext.Projects.Include(x => x.Location).DefaultIfEmpty()...;

答案 1 :(得分:0)

在T-SQL中,左连接和左外连接是相同的。外键字是可选的。您提供的查询将返回所有产品,无论他们是否有位置。测试一下,看看。

有关详细信息,请参阅:LEFT JOIN vs. LEFT OUTER JOIN in SQL Server

答案 2 :(得分:0)

我遇到了这种情况,除了生成的SQL是:

INNER JOIN [Blah.[Table] AS [x.Child] ON [x].[ChildId] = [x.Child].[Id]

这确实导致了问题,而问题LEFT JOIN会没问题(正如其他人指出的那样)。

那么为什么会这样呢?我的ChildIdGuid,我确保将其设为Guid?,因为可空性使其成为可选项。

我接下来尝试将.IsRequired(false)添加到ChildId的映射中。这给出了一个错误,告诉我实际问题是什么:我还在主键中包含ChildId(不必要)。

从主键中删除它会导致查询更改为LEFT JOIN,一切都很好。

答案 3 :(得分:-2)

尝试将 entity.HasOne(project =&gt; project.Location).WithMany()更改为 entity.HasOptional(project =&gt; project.Location).WithMany()

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{

    // Configure Student & StudentAddress entity
    modelBuilder.Entity<Student>()
                .HasOptional(s => s.Address) // Mark Address property optional in Student entity
                .WithRequired(ad => ad.Student); // mark Student property as required in StudentAddress entity. Cannot save StudentAddress without Student

}

检查此链接configure-one-to-one-relationship-in-code-firstentity_framework-navigation_property_basics_with_code_first