问题描述
我在嵌套实体和实体框架核心中的一些奇怪行为方面遇到问题。
我应该从数据库表中加载实体会议。会议有多个属性,但只有位置给我带来了麻烦。 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 在拥有的类型上是多余的。 :-)