今天主要介绍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中值对象的更多相关文章

  1. 关于项目中值对象Identifier的设计-领域驱动

    到现在为止做了不项目,发现每个实体都会有个相应的值对象. 先简单说一下值对象和实体之间的区别: (以下内容来着<领域驱动设计>一书) 当一个小孩画画的时候,他注意的是画笔的颜色和笔尖的粗细 ...

  2. EFCore:关于DDD中值对象(Owns)无法更新数值

    最近使用DDD+EFCore时,使用EFCore提供的OwnsOne或者OwnsMany关联值对象保存数据,没想到遇到一个很奇怪的问题:值对象中的值竟然无法被EFCore保存!也没有抛出任何异常!我瞬 ...

  3. DDD 领域驱动设计-Value Object(值对象)如何使用 EF 进行正确映射

    写在前面 首先,这篇博文是用博客园新发布的 MarkDown编辑器 编写的,这也是我第一次使用,语法也不是很熟悉,但我觉得应该会很爽,博文后面再记录下用过的感受,这边就不多说. 阅读目录: 上一篇回顾 ...

  4. Value Object(值对象)如何使用 EF 进行正确映射

    DDD 领域驱动设计-Value Object(值对象)如何使用 EF 进行正确映射 写在前面 首先,这篇博文是用博客园新发布的 MarkDown编辑器 编写的,这也是我第一次使用,语法也不是很熟悉, ...

  5. [原创]java WEB学习笔记59:Struts2学习之路---OGNL,值栈,读取对象栈中的对象的属性,读取 Context Map 里的对象的属性,调用字段和方法,数组,list,map

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  6. DDD Code First 迁移数据实现EF CORE的软删除,值对象迁移配置

    感谢Jeffcky大佬的博客: EntityFramework Core 2.0全局过滤 (HasQueryFilter) https://www.cnblogs.com/CreateMyself/p ...

  7. DDD实战12 值对象不创建表,而是直接作为实体中的字段

    这里的值对象如下风格: namespace Order.Domain.PocoModels { //订单地址 //虽然是值对象 但是不继承ValueObject //因为继承ValueObject会有 ...

  8. 《Entity Framework 6 Recipes》中文翻译系列 (44) ------ 第八章 POCO之POCO中使用值对象和对象变更通知

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 8-4  POCO中使用值对象(Complex Type--也叫复合类型)属性 问题 ...

  9. DDD 领域驱动设计-“臆想”中的实体和值对象

    其他博文: DDD 领域驱动设计-三个问题思考实体和值对象 DDD 领域驱动设计-三个问题思考实体和值对象(续) 以下内容属于博主"臆想",如有不当,请别当真. 扯淡开始: 诺兰的 ...

随机推荐

  1. python不同模式打开文件的完全列表

    模式 描述 r 以只读方式打开文件.文件的指针将会放在文件的开头.这是默认模式. rb 以二进制格式打开一个文件用于只读.文件指针将会放在文件的开头.这是默认模式. r+ 打开一个文件用于读写.文件指 ...

  2. linux如何查看系统信息

    一:cpu [root@srv /]# more /proc/cpuinfo | grep "model name" model name    : Intel(R) Xeon(R ...

  3. select()函数以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET

    http://hi.baidu.com/%B1%D5%C4%BF%B3%C9%B7%F0/blog/item/e7284ef16bcec3c70a46e05e.html select函数用于在非阻塞中 ...

  4. WSAStartup

    WSAStartup,是Windows Sockets Asynchronous的启动命令.Windows下的网络编程接口软件 Winsock1 或 Winsock2 里面的一个命令. 外文名 WSA ...

  5. easyUI文本框textbox笔记

    知识点: 1.图标位置 Icon Align属性,有left和right两个: 2.textbox的setvalue方法,getvalue方法. <div style="margin: ...

  6. angularjs笔记(二)

    AngularJS API 4.AngularJS过滤器 使用一个管道符(|)添加到表达式和指令中 例1.格式化字母转为大写 <!DOCTYPE html> <html> &l ...

  7. VclZip压缩文件夹

    压缩指定路径MyZipDir下的文件夹b及b目录下的所有文件和文件b.txt function ZipDir(zipMode:Integer;zipControl:TVCLZip;MyZipName, ...

  8. Java数据结构——用双端链表实现队列

    //================================================= // File Name : LinkQueue_demo //---------------- ...

  9. Linux下which、whereis、locate、find 命令的区别

    1.which 作用:查看可执行文件的位置(通过 PATH环境变量到该路径内查找可执行文件) 语法:which 可执行文件名称 示例: zsm@wilburUbun:/$ which passwd / ...

  10. LaTex 使用 - 配置

    Reference Link: http://www.howtotex.com/howto/installing-latex-on-windows/ MikTeX:http://miktex.org/ ...