Skip to main content

跟踪关联引用

场景

我们再来看一个关联的例子。每辆小汽车都会安装4个轮子,因此两者之间存在一个关联,而且还是一种特殊的关联,称为“聚合” 现在的业务需求是更换其中的一个轮子,请问你会如果实现这个需求? 你可能很快会想到如下做法:

public void ReplaceWheel(Wheel current, Wheel newOne){
currentCarld = 0;
newOne.Carld = carld;
}

执行上述方法后,再调用持久化框架实施持久化。这种方案存在两个问题。第一,需要在Wheel类上定义 carld属性并为之设置访问器,但一般来说在组合关系中不需要定义这个属性,这里纯粹是为了实现存储而定义的,与前面讲述的一样,也属于领域模型向存储做出的妥协。第二,不太符合面向对象累址,本质上仍然是关系数据库的思维,只是把“字段”换成了“属性”,换了一个马甲,背后的思维理念没有变化。

image-20231031201456025

解决方案

符合面向对象思维的的做法是这样的:

public void ReplaceWheel(Wheel current, Wheel newOne)
{
wheels.Remove(current);
wheels.Add(newOne);
}

执行上述方法后,再调用持久化和架实施持久化。 这种方案就需要我们的持久化框架能够跟踪关联引用的变化,并进而推断出关联实例的销毁与构建。这是种高级的用法。

解决方案

实体模型

 /// <summary>
///汽车
/// </summary>
public class Car
{
/// <summary>
///编号
/// </summary>
private string _carCode;

/// <summary>
///名称
/// </summary>
private string _carName;

/// <summary>
///汽车的车轮
/// </summary>
private List<Wheel> _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<Wheel> CarWheels
{
get => _carWheels;
set => _carWheels = value;
}

/// <summary>
///换轮胎
/// </summary>
/// <param name="oldWheel">旧轮胎</param>
/// <param name="newWheel">新轮胎</param>
public void ReplaceWheel(Wheel oldWheel, Wheel newWheel)
{
if (_carWheels == null)
_carWheels = new List<Wheel>();

_carWheels.RemoveAll(p => p.WheelCode == oldWheel.WheelCode);
_carWheels.Add(newWheel);
}
}
/// <summary>
///表示车轮
/// </summary>
public class Wheel
{
/// <summary>
///车轮编号
/// </summary>
private string _wheelCode;

/// <summary>
///车轮编号
/// </summary>
public string WheelCode
{
get => _wheelCode;
set => _wheelCode = value;
}
}

模型配置

public class TrackAssociationReferenceConfiguration : 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 carAssWheel = modelBuilder.Association<Car, Wheel>();
//配置关联端 End1为Car 在关联表中Car的主键CarCode映射为CarCode
carAssWheel.AssociationEnd(p => p.End1).HasMapping("CarCode", "CarCode");
//配置关联端 End2为Wheel 在关联表中Wheel的主键WheelCode映射为WheelCode
carAssWheel.AssociationEnd(p => p.End2).HasMapping("WheelCode", "WheelCode");
//没有独立映射表 和 Wheel存储在一起
carAssWheel.ToTable("Wheel");

//配置汽车实体型中的关联引用 左端就是关联型上自己类型的这一端 即End1 右端即为另外一端End2
carEntity.AssociationReference(p => p.CarWheels).HasLeftEnd("End1").HasRightEnd("End2");
}
}