显式关联的伴随映射
场景
接下来我们看一个涉及显式关联伴随映射的例子。汽车与车轮之间存在“所属”关联,而车轮安装的位置(左前、右前、左后、右后等)则属于这个关联的属性。注意,它不是车轮的属性,因为同一车轮安装在不同车子上可能位置不同,甚至是安装同一车上但不同时期的位置也不同。 因为是一对多关联,一般不会设置关联表,而是把关联存储于多方 (车轮)映射的表。在这种情况下,使用传统ORM框架就必须变通处理,也就是说,为了迎合数据库结构模型中就不能保留关联类(CarWheel),关联类的属性必须归入多方的实体类,这样模型就丢掉了非常重要的领域知识。那么正确的处理方式是什么呢?
解决方案
实体模型
/// <summary>
///汽车
/// </summary>
public class Car
{
/// <summary>
///编号
/// </summary>
private string _carCode;
/// <summary>
///名称
/// </summary>
private string _carName;
/// <summary>
///汽车的车轮
/// </summary>
private List<CarWheel> _carWheels;
/// <summary>
///编号
/// </summary>
public string CarCode
{
get => _carCode;
set => _carCode = value;
}
/// <summary>
///名称
/// </summary>
public string CarName
{
get => _carName;
set => _carName = value;
}
/// <summary>
///汽车的车轮
/// </summary>
public List<CarWheel> CarWheels
{
get => _carWheels;
set => _carWheels = value;
}
/// <summary>
///根据车的车轮位置获取车轮
/// </summary>
/// <param name="wheelPosition">车轮位置</param>
/// <returns>车轮</returns>
public Wheel GetWheel(WheelPosition wheelPosition)
{
return _carWheels.First(p => p.WheelPosition == wheelPosition).Wheel;
}
}
/// <summary>
///表示汽车的车轮
/// </summary>
public class CarWheel
{
/// <summary>
///汽车
/// </summary>
private Car _car;
/// <summary>
///车轮
/// </summary>
private Wheel _wheel;
/// <summary>
///车轮位置
/// </summary>
private WheelPosition _wheelPosition;
/// <summary>
///初始化汽车轮胎
/// </summary>
/// <param name="car">汽车</param>
/// <param name="wheel">车轮</param>
/// <param name="wheelPosition">位置</param>
public CarWheel(Car car, Wheel wheel, WheelPosition wheelPosition)
{
_car = car;
_wheel = wheel;
_wheelPosition = wheelPosition;
}
/// <summary>
///反持久化构造函数
/// </summary>
protected CarWheel()
{
}
/// <summary>
///汽车
/// </summary>
public Car Car
{
get => _car;
set => _car = value;
}
/// <summary>
///车轮
/// </summary>
public Wheel Wheel
{
get => _wheel;
set => _wheel = value;
}
/// <summary>
///车轮位置
/// </summary>
public WheelPosition WheelPosition
{
get => _wheelPosition;
set => _wheelPosition = value;
}
}
/// <summary>
///表示车轮
/// </summary>
public class Wheel
{
/// <summary>
///车轮编号
/// </summary>
private string _wheelCode;
/// <summary>
///车轮编号
/// </summary>
public string WheelCode
{
get => _wheelCode;
set => _wheelCode = value;
}
}
/// <summary>
///表示车轮的位置
/// </summary>
public enum WheelPosition
{
/// <summary>
///左前
/// </summary>
FrontLeft,
/// <summary>
///右前
/// </summary>
FrontRight,
/// <summary>
///左后
/// </summary>
BackLeft,
/// <summary>
///右后
/// </summary>
BackRight
}
模 型配置
public class ExplicitlyCompionConfiguration : MySqlContextConfigProvider
{
/// <summary>由派生类实现,获取数据库连接字符串。</summary>
protected override string ConnectionString => Configuration.MySqlConnectionString;
/// <summary>获取一个值,该值指示是否启用存储结构映射</summary>
protected override bool EnableStructMapping => true;
/// <summary>使用指定的建模器创建对象数据模型。</summary>
/// <param name="modelBuilder"></param>
protected override void CreateModel(ModelBuilder modelBuilder)
{
//配置汽车的实体型
var carEntity = modelBuilder.Entity<Car>();
//配置主键
carEntity.HasKeyAttribute(p => p.CarCode).HasKeyIsSelfIncreased(false);
//配置车轮实体型
var wheelEntity = modelBuilder.Entity<Wheel>();
//配置主键
wheelEntity.HasKeyAttribute(p => p.WheelCode).HasKeyIsSelfIncreased(false);
//配置汽车车轮 显式关联型 默认是存储在类名的表内 也可以指定为伴随存储
var carWheelAssociation = modelBuilder.Association<CarWheel>();
//配置一个反持久化构造函数
carWheelAssociation.HasConstructor(typeof(CarWheel).GetConstructor(
BindingFlags.Instance | BindingFlags.NonPublic,
null, Type.EmptyTypes, null)).End();
//配置关联端 汽车关联端为Car这个属性 在关联表中Car的主键CarCode映射为CarCode
carWheelAssociation.AssociationEnd(p => p.Car).HasMapping("CarCode", "CarCode");
//配置关联端 车轮关联端为Wheel这个属性 在关联表中Wheel的主键WheelCode映射为WheelCode
carWheelAssociation.AssociationEnd(p => p.Wheel).HasMapping("WheelCode", "WheelCode");
//没有独立映射表 和 Wheel存储在一起
carWheelAssociation.ToTable("Wheel");
//配置汽车实体型中的关联引用 左端就是关联型上自己类型的这一端 即Car 右端即为另外一端Wheel
carEntity.AssociationReference(p => p.CarWheels).HasLeftEnd("Car").HasRightEnd("Wheel");
}
}