我必须使用以下架构(看起来像RoR样式?)来支持第三方数据库。
这是主表Vehicle,其中VehicleType是鉴别符(可能的值为'Car'
和'Bike'
)。因此,如果VehicleType = 'Car'
,则SubId指向要记录在Car表中,如果VehicleType = 'Bike'
,则将指向记录到Bike表中。
而且plus表不共享主键值。
我想避免代码中的复杂逻辑,但是我不明白是否可以在此处使用继承(因此Car : Vehicle
和Bike : Vehicle
)或至少使用导航属性(因此Car.Vehicle
和Bike.Vehicle
可以工作)。所以问题是我可以使用其中的一些吗?如何实现呢?
最佳答案
表不共享主键值
因此,无法将其映射到一个应该为TPT的继承模式。不同的主键值可以防止这种情况。
剩下的是映射相互连接的不同类,因此您至少可以使用导航属性,而不是手动加入不相关的实体。我找到了一种方法来执行此操作,但这并不理想。实际上,这是非常人为的。但是,看看它是否对您来说是可行的解决方案。
我使用了这些类:
public abstract class Vehicle
{
public int VehicleId { get; set; }
public string Name { get; set; }
public VehicleInfo VehicleInfo { get; set; }
}
public class CarVehicle : Vehicle
{ }
public class BikeVehicle : Vehicle
{ }
public abstract class VehicleInfo
{
public int ID { get; set; }
}
public class CarInfo : VehicleInfo
{
public string Model { get; set; }
}
public class BikeInfo : VehicleInfo
{
public bool IsEbike { get; set; }
}
我通过TPH将表
Vehicle
分为实体CarVehicle
和BikeVehicle
:modelBuilder.Entity<Vehicle>().ToTable("Vehicle");
modelBuilder.Entity<Vehicle>()
.Map<CarVehicle>(m => m.Requires("VehicleType").HasValue("Car"))
.Map<BikeVehicle>(m => m.Requires("VehicleType").HasValue("Bike")
.HasColumnType("CHAR")
.HasMaxLength(4));
另一方面,
CarInfo
和BikeInfo
必须具有自己的表,因此在这里我使用了TPC:modelBuilder.Entity<VehicleInfo>()
.Map<CarInfo>(c =>
{
c.MapInheritedProperties().ToTable("Car");
c.Property(x => x.ID).HasColumnName("CarId");
c.Property(x => x.Model);
})
.Map<BikeInfo>(c =>
{
c.MapInheritedProperties().ToTable("Bike");
c.Property(x => x.ID).HasColumnName("BikeId");
c.Property(x => x.IsEbike);
});
最后,连接类:
modelBuilder.Entity<Vehicle>().HasRequired(c => c.VehicleInfo)
.WithOptional().Map(m => m.MapKey("SubId"));
通过使用基本类型
VehicleInfo
,可以通过一个外键Vehicle.VehicleInfo
映射一个属性SubId
。这是不可能的:public class CarVehicle : Vehicle
{
public CarInfo CarInfo { get; set; }
}
public class BikeVehicle : Vehicle
{
public BikeInfo BikeInfo { get; set; }
}
如果您尝试通过
SubId
映射两个导航属性,则EF不会接受。该模型的主要缺点是,您始终必须将“ Info”实体作为
VehicleInfo
对象。 EF将创建正确的子类型,但其编译时类型为VehicleInfo
。同样,如果设置VehicleInfo
属性,则必须记住提供正确的类型。您可以通过传递属性来缓解这种情况...public class CarVehicle : Vehicle
{
[NotMapped]
public CarInfo CarInfo
{
get { return VehicleInfo as CarInfo; }
set { VehicleInfo = value; }
}
}
...与
BikeVehicle
中的相同。但是您将不能直接在LINQ查询中使用此属性,因为它没有(也不能被映射)。此外,从简单的东西中执行的SQL ...
db.Set<CarVehicle>().Include(c => c.VehicleInfo).ToList();
...之所以如此庞大是因为EF在所有继承树中进行了挖掘。
关于c# - 不共享EF中主键的多态关联,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/37235858/