嵌套实体的加载

Loading of nested entities

我在 Entity FrameWork Core 中遇到嵌套实体和一些奇怪行为的问题。

我应该从数据库中加载实体会议 table。会议有多个属性,但只有位置给我带来麻烦。位置存储 a - 位置 ;-) 会议在下面进行了简化。 位置要简单得多,它包含名称、描述、GPS 坐标和地址。见下文 Address 包含一个地址,尽管 class 有更多属性,但在下面进行了简化。

从数据库中检索会议时,一切正常。除非极少数情况下 Name、Description 和 GPSCoordinates 都为 null,否则无论 Address 中存储的任何值如何,Location 都为 null。

如果我将 Name 从 Null 更改为空字符串“”,那么没问题,一切都会按预期加载。

会议、地点和地址的所有值都存储在 flattend 数据库中 table。 table 使用 Fluent Api 配置。另一个 class 课程继承 Meeting 并存储在同一个 table 中 - 因此是鉴别器 - 但它应该没有影响。

为简单起见,减少了以下代码

public class Meeting : BaseEntity<long>
{
    public string Name { get; set; }
    public Location Location { get; set; }
}

public class Location
{
    public string Name { get; set; }
    public string Description { get; set; }
    public PostalAddress Address { get; set; }
    public Point GpsCoordinates { get; set; }
}

public class PostalAddress
{
    public string StreetAddress1 { get; set; }
    public string ZipCode { get; set; }
    public string City { get; set; }
    public string Country { get; set; }
}

Meeting meet = context.meetDbSet.Include(p=>p.Location).ThenInclude(p=>p.Address).FirstOrDefault();


public void Configure(EntityTypeBuilder<Meeting> builder)
{
    builder
        .ToTable("Meetings")
        .HasKey(p => p.Id);

    builder
        .HasDiscriminator<string>("Meeting_Descriminator")
        .HasValue<Meeting>("")
        .HasValue<Course>("Course");

    builder
        .OwnsOne(p => p.Location, location =>
            {
                location
                    .Property(p => p.Name)
                    .HasColumnType("nvarchar(max)")
                    .IsRequired(false);

                location
                    .Property(p => p.Description)
                    .HasColumnType("nvarchar(max)")
                    .IsRequired(false);

                location
                    .OwnsOne(p => p.Address, postaladdress =>
                        {
                            postaladdress
                                .Property(p => p.StreetAddress1)
                                .HasColumnName("StreetAddress1")
                                .HasColumnType("nvarchar(max)")
                                .IsRequired(false);
                            postaladdress
                                .Property(p => p.ZipCode)
                                .HasColumnType("nvarchar(max)")
                                .IsRequired(false);
                            postaladdress
                                .Property(p => p.City)
                                .HasColumnName("City")
                                .HasColumnType("nvarchar(max)")
                                .IsRequired(false);
                            postaladdress
                                .Property(p => p.Country)
                                .HasColumnName("Country")
                                .HasColumnType("nvarchar(max)")
                                .IsRequired(false);
                        });

                    location
                        .Property(p => p.GpsCoordinates)
                        .HasColumnType("geography")
                        .IsRequired(false);
            });
}

我认为这可能是因为您使用的是自有类型。对于拥有的类型,类型的键被定义为其属性的组合。当所有属性都为null时,无法定义key,地址也不会被加载。但是当使用自有类型时,这种行为听起来合乎逻辑。当拥有某物时,当父 属性 不存在时,您假设它不存在。如果不是这样,你应该改变结构。

终于找到解决办法了。事实证明这是 EF Core 中的一个问题,但是在位置上添加导航 属性 并使 属性 成为必需的技巧。

注意它必须在它自己拥有的类型之后配置。

public void Configure(EntityTypeBuilder<Meeting> builder)
{

builder
    .ToTable("Meetings")
    .HasKey(p => p.Id);

builder
    .HasDiscriminator<string>("Meeting_Descriminator")
    .HasValue<Meeting>("")
    .HasValue<Course>("Course");

builder
    .OwnsOne(p => p.Location, location =>
        {
            location
                .Property(p => p.Name)
                .HasColumnType("nvarchar(max)")
                .IsRequired(false);

            location
                .Property(p => p.Description)
                .HasColumnType("nvarchar(max)")
                .IsRequired(false);

            location
                .OwnsOne(p => p.Address, postaladdress =>
                    {
                        postaladdress
                            .Property(p => p.StreetAddress1)
                            .HasColumnName("StreetAddress1")
                            .HasColumnType("nvarchar(max)")
                            .IsRequired(false);
                        postaladdress
                            .Property(p => p.ZipCode)
                            .HasColumnType("nvarchar(max)")
                            .IsRequired(false);
                        postaladdress
                            .Property(p => p.City)
                            .HasColumnName("City")
                            .HasColumnType("nvarchar(max)")
                            .IsRequired(false);
                        postaladdress
                            .Property(p => p.Country)
                            .HasColumnName("Country")
                            .HasColumnType("nvarchar(max)")
                            .IsRequired(false);
                    });

                location
                    .Property(p => p.GpsCoordinates)
                    .HasColumnType("geography")
                    .IsRequired(false);
        });
builder
    .Navigation(p => p.Location)
    .IsRequired(true);
}

还有更多。在寻找解决方案时,我还了解到 Include 在拥有的类型上是多余的。 :-)