加载嵌套实体

问题描述

我在嵌套实体和实体框架核心中的一些奇怪行为方面遇到问题。

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

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

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

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

为了简单起见,下面的代码被简化了

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);
            });
}

解决方法

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

,

我终于找到了解决办法。事实证明,这是 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 在拥有的类型上是多余的。 :-)