Code First05--CodeFirst中值对象
今天主要介绍EF Code First中一个高级部分:Value Object,中文翻译过来叫做值对象。
所谓的值对象就是一些没有生命周期,也没有业务逻辑上唯一标识符的类。哪些类是Entity,哪些类是Value Object不是固定的,取决于具体的业务逻辑。比如说Customer这个类,如果在CRM系统当中,它是最重要的信息,我们需要跟踪它的状态,管理它的生命周期。但是在其他系统中,客户信息可能只代表一个名字和一些其他的属性。
下面我有一个需求,我们的订单系统有一个Customer类。在我们以前的示例中,Customer类的Address属性是一个字符串。现在我们的业务逻辑发生了改变,需要把地址信息分类显示和保存,把地址信息细分为国家,省,城市,街道及门牌号,还有邮政编码。在我们的业务中,地址信息仅仅是一些属性的集合,不需要跟踪它的生命周期,也不存在业务中的唯一标识符。所以我们把它定义为一个Value Object:
public class Address
{
public string Country { get; set; }
public string Province { get; set; }
public string City { get; set; }
public string StreetAddress { get; set; }
public string ZipCode { get; set; }
}
想要让Entity Framework Code First默认地识别出值对象,我们的类必须具备三个条件:
1.值对象的类不能有主键。
2.值对象的类只能包含.net基础类型的属性。
3.使用值对象的类,只能包含值对象的一个实例,不能使用值对象的集合。
然后我们需要改变我们的Customer类,使用Address值对象来替代以前的字符串。
public class Customer
{ public string IDCardNumber { get; set; } public string CustomerName { get; set; } public string Gender { get; set; } public Address Address { get; set; } public string PhoneNumber { get; set; } }
如果值对象不满足三个默认的值对象识别条件,我们就需要在我们自定义的DbContext类的OnModelCreating方法中声明Address是一个值对象。
modelBuilder.ComplexType<Address>();
以下为一个测试的方法,看他是怎么样生成到数据库的表的:
[TestMethod]
public void CanAddNewCustomerWithAddress()
{
OrderSystemContext unitOfWork = new OrderSystemContext();
CustomerRepository repository = new CustomerRepository(unitOfWork);
Customer newCustomer = new Customer() { IDCardNumber = "", CustomerName = "Alex", Gender = "M", PhoneNumber = "test" }
Address customerAddress = new Address { Country = "China", Province = "Tianjin", City = "Tianjin", StreetAddress = "Crown Plaza", ZipCode = "" };
newCustomer.Address = customerAddress;
repository.AddNewCustomer(newCustomer);
unitOfWork.CommitChanges();
}
我们使用可插入基础数据的DropCreateOrderDatabaseWithSeedValueAlways自定义数据库初始化类,每次执行测试方法之前都重新建立数据库。
[TestInitialize]
public void InitializeCustomerRepositoryTest()
{
Database.SetInitializer(new DropCreateOrderDatabaseWithSeedValueAlways());
}
可以看到生成的对应表结构如下:



由上我们可以看到,我们可以看到Code First默认会把Address值对象的属性作为Customers表的列。列的名字默认是值对象类的名字+“_”+值对象属性的名字。
public class AddressComplexTypeConfiguration:ComplexTypeConfiguration<Address> {
public AddressComplexTypeConfiguration()
{
Property(a => a.Country).HasColumnName("Country").HasMaxLength();
Property(a => a.Province).HasColumnName("Province").HasMaxLength();
Property(a => a.City).HasColumnName("City").HasMaxLength();
Property(a => a.StreetAddress).HasColumnName("StreetAddress").HasMaxLength();
Property(a => a.ZipCode).HasColumnName("ZipCode").HasMaxLength();
}
}
我们通过HasColumnName这个方法,可以改变属性对应的列的名字。Entity Framework Code First默认会使用类中属性的名字作为列的名字。

总结:本章主要还是讲的是Fluent API的配置映射,如何生成对应的数据库的表结构。
Code First05--CodeFirst中值对象的更多相关文章
- 关于项目中值对象Identifier的设计-领域驱动
到现在为止做了不项目,发现每个实体都会有个相应的值对象. 先简单说一下值对象和实体之间的区别: (以下内容来着<领域驱动设计>一书) 当一个小孩画画的时候,他注意的是画笔的颜色和笔尖的粗细 ...
- EFCore:关于DDD中值对象(Owns)无法更新数值
最近使用DDD+EFCore时,使用EFCore提供的OwnsOne或者OwnsMany关联值对象保存数据,没想到遇到一个很奇怪的问题:值对象中的值竟然无法被EFCore保存!也没有抛出任何异常!我瞬 ...
- DDD 领域驱动设计-Value Object(值对象)如何使用 EF 进行正确映射
写在前面 首先,这篇博文是用博客园新发布的 MarkDown编辑器 编写的,这也是我第一次使用,语法也不是很熟悉,但我觉得应该会很爽,博文后面再记录下用过的感受,这边就不多说. 阅读目录: 上一篇回顾 ...
- Value Object(值对象)如何使用 EF 进行正确映射
DDD 领域驱动设计-Value Object(值对象)如何使用 EF 进行正确映射 写在前面 首先,这篇博文是用博客园新发布的 MarkDown编辑器 编写的,这也是我第一次使用,语法也不是很熟悉, ...
- [原创]java WEB学习笔记59:Struts2学习之路---OGNL,值栈,读取对象栈中的对象的属性,读取 Context Map 里的对象的属性,调用字段和方法,数组,list,map
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...
- DDD Code First 迁移数据实现EF CORE的软删除,值对象迁移配置
感谢Jeffcky大佬的博客: EntityFramework Core 2.0全局过滤 (HasQueryFilter) https://www.cnblogs.com/CreateMyself/p ...
- DDD实战12 值对象不创建表,而是直接作为实体中的字段
这里的值对象如下风格: namespace Order.Domain.PocoModels { //订单地址 //虽然是值对象 但是不继承ValueObject //因为继承ValueObject会有 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (44) ------ 第八章 POCO之POCO中使用值对象和对象变更通知
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 8-4 POCO中使用值对象(Complex Type--也叫复合类型)属性 问题 ...
- DDD 领域驱动设计-“臆想”中的实体和值对象
其他博文: DDD 领域驱动设计-三个问题思考实体和值对象 DDD 领域驱动设计-三个问题思考实体和值对象(续) 以下内容属于博主"臆想",如有不当,请别当真. 扯淡开始: 诺兰的 ...
随机推荐
- HTML5学习总结-05 HTML5表单
一 HTML5 新的类型 HTML5 拥有多个新的表单输入类型.这些新特性提供了更好的输入控制和验证. email url number range Date pickers (date, month ...
- Ubuntu学习总结-04 搭建JAVA开发环境
JAVA开发环境是一种跨平台的程序设计语言,可以在windows.LINUX等操作系统上进行开发. 1 下载JDK 从以下地址下所需的jdk安装包 . http://www.oracle.com/te ...
- python redis使用心得
发布与订阅 连接池代码 redis_conn.py import redis REDIS_CONN = { 'HOST': '192.168.1.11', 'PORT': '6378', 'DB': ...
- TeamViewer12.0.71503(远程控制软件)精简版单文件企业版介绍
TeamViewer 是一款能在任何防火墙和 NAT 代理的后台用于远程控制,桌面共享和文件传输的简单且快速的解决方案.为了连接到另一台计算机,只需要在两台计算机上同时运行 TeamViewer 即可 ...
- 转:遗传算法解决TSP问题
1.编码 这篇文章中遗传算法对TSP问题的解空间编码是十进制编码.如果有十个城市,编码可以如下: 0 1 2 3 4 5 6 7 8 9 这条编码代表着一条路径,先经过0,再经过1,依次下去. 2.选 ...
- 深入浅出MyBatis
参考文献:深入浅出MyBatis MyBatis功能架构图: MyBatis内部原理流程图: 详情见:深入浅出MyBatis
- HTTP2.0的二进制分帧
1.帧的类型: 在二进制分帧的结构中,头部有8个字节(64Bit),其中有一个字节(8Bit)来标志帧的类型: HTTP2.0规定了如下帧类型: DATA: 用于传输HTTP消息体 HEADERS:用 ...
- 计算div里面li个数
方式一:js var content=document.getElementById("content"); alert(document.getElementsByTagName ...
- Linux的io机制
Linux的io机制 Buffered-IO 和Direct-IO Linux磁盘I/O分为Buffered IO和Direct IO,这两者有何区别呢? 对于Buffered IO: 当应用程序尝试 ...
- AndroidStudio-使用Translations Editor
前言 如果你的App支持多语言,你需要正确的管理你的翻译字符串资源.Android Studio提供了翻译编辑器使更容易的查看和管理翻译资源. 关于翻译编辑器 翻译资源存储工程的多个目录下的多个XML ...