1 理解值对象
讨论值对象,先简单介绍下实体,通俗的理解:实体是由唯一标识和一系列属性构造的类,实体的核心是用唯一标识来定义的,而不是通过属性来定义的。即使属性完全相同也可能是两个不同的对象。实体本身是有状态的,有生命周期,实体本身会提箱相关的业务行为,业务行为会使实体属性或状态发生改变和影响
值对象本身是无状态的,不可变,没有唯一标识,从这个层面上来讲,值对象可以理解为实际的Entity对象的一个属性的结合,该值对象附属在一个实际的实体对象上面。值对象本省不存在一个独立的生命周期,也一般不会产生独立的行为
如上图所示,一个聚合根通常是由多个属性构成,比如Order这个实体(在这里是聚合根)由一系列的属性构成:唯一标识ID,OrderDate,OrderItems ,Address等,其中Address是一个对象引用,它有Street,City,country等属性构成,但是并没有唯一标识,因此Address被模型化为一个值对象。
2 值对象特性
没有唯一标识
不变性
在值对象中,不关心标识,只要我们能确定值对象的属性值都一样,我们就可以说这两个值对象是相同的。
比如说两个学生的家庭地址(省市县街道门牌号)是一样的,我们就可以认为是同一个地址,者就是相等性比较。
如果学生在修改地址的时候,无论修改的省或者市或者县,我们不关心改了多少,而是 将整个值对象覆盖,这就是值对象的不变性和可替换性
更好的表达业务概念
例如:有一个Student表,有 Name,Age,Gender,Province,City,Street 等属性
可以有两种实现方式:
方式一:将这些属性全部生命在一个Student实体中
方式二:将Provice City Street等表达地址的信息独立出来,包装成一个值对象
复制代码
public class Student : Entity
{
//.....其他属性
/// <summary>
/// 地址外键
/// </summary>
public Address Address { get; private set; }
}
/// <summary>
/// 地址
/// </summary>
public class Address :Entity {/// <summary>
/// 省份
/// </summary>
public string Province { get; private set; }
/// <summary>
/// 城市
/// </summary>
public string City { get; private set; }
}
显然方式二更能够清楚的表达业务
当决定一个领域概念是否是一个值对象的时候,需要考虑是否具有以下特性:
1 它描述了领域中的一个东西
2 可以作为一个不变量
3 当它改变时,可以用另一个值对象替换
4 可以和别的值对象进行相等性比较
3 实体和值对象区别
1 实体有标识,值对象没有
2 相等性测试方式不同。实体根据标识判断,值对象根据内部所有属性值判断
3 实体匀速变化,值对象不允许变化
4 持久化的映射方式不同。实体采用单表继承、类表继承、具体表继承来映射类层次结构,而值对象采用嵌入式或序列化大对象方式映射
4 EF/EFCore实现值对象
1.EF中实现值对象和普通entity没有什么区别,因为需要持久层到数据库中,仍然需要唯一标识。
2.EFCore 对值对象实现了良好的支持。
EFCore1.1使用值对象需要在DbContext中为值对象指定隐藏的唯一标识
EFCore2.0及以后版本出现了 owned entity type (自引用实体类型),需要在实体的Configuration中配置一下即可,如下:
值对象是单个对象情况:
值对象是List的情形: