扩大
缩小
  

Code First05--CodeFirst中值对象

今天主要介绍EF Code First中一个高级部分:Value Object,中文翻译过来叫做值对象。

所谓的值对象就是一些没有生命周期,也没有业务逻辑上唯一标识符的类。哪些类是Entity,哪些类是Value Object不是固定的,取决于具体的业务逻辑。比如说Customer这个类,如果在CRM系统当中,它是最重要的信息,我们需要跟踪它的状态,管理它的生命周期。但是在其他系统中,客户信息可能只代表一个名字和一些其他的属性。

 

下面我有一个需求,我们的订单系统有一个Customer类。在我们以前的示例中,Customer类的Address属性是一个字符串。现在我们的业务逻辑发生了改变,需要把地址信息分类显示和保存,把地址信息细分为国家,省,城市,街道及门牌号,还有邮政编码。在我们的业务中,地址信息仅仅是一些属性的集合,不需要跟踪它的生命周期,也不存在业务中的唯一标识符。所以我们把它定义为一个Value Object:

public class Address
    {
        public string Country { getset; }
        public string Province { getset; }
        public string City { getset; }
        public string StreetAddress { getset; }
        public string ZipCode { getset; }
    }

想要让Entity Framework Code First默认地识别出值对象,我们的类必须具备三个条件:

1.值对象的类不能有主键。

2.值对象的类只能包含.net基础类型的属性。

3.使用值对象的类,只能包含值对象的一个实例,不能使用值对象的集合。

 

然后我们需要改变我们的Customer类,使用Address值对象来替代以前的字符串。

 

 public class Customer

 {
        public string IDCardNumber { getset; }
        public string CustomerName { getset; }
        public string Gender { getset; }
        public Address Address { getset; }
        public string PhoneNumber { getset; }
}  

 

如果值对象不满足三个默认的值对象识别条件,我们就需要在我们自定义的DbContext类的OnModelCreating方法中声明Address是一个值对象。

 

modelBuilder.ComplexType<Address>();


以下为一个测试的方法,看他是怎么样生成到数据库的表的:

 [TestMethod]

public void CanAddNewCustomerWithAddress()
{
            OrderSystemContext unitOfWork = new OrderSystemContext();
            CustomerRepository repository = new CustomerRepository(unitOfWork);
            Customer newCustomer = new Customer() { IDCardNumber = "120104198106072518", CustomerName = "Alex", Gender = "M", PhoneNumber = "test" }
            Address customerAddress = new Address { Country = "China", Province = "Tianjin", City = "Tianjin", StreetAddress = "Crown Plaza", ZipCode = "300308" };
            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(100);
            Property(a => a.Province).HasColumnName("Province").HasMaxLength(100);
            Property(a => a.City).HasColumnName("City").HasMaxLength(100);
            Property(a => a.StreetAddress).HasColumnName("StreetAddress").HasMaxLength(500);
            Property(a => a.ZipCode).HasColumnName("ZipCode").HasMaxLength(6);
        }
    }

 我们通过HasColumnName这个方法,可以改变属性对应的列的名字。Entity Framework Code First默认会使用类中属性的名字作为列的名字。

 

 

总结:本章主要还是讲的是Fluent API的配置映射,如何生成对应的数据库的表结构。

 

posted @ 2015-07-23 18:27  风筝遇上风  阅读(291)  评论(0编辑  收藏  举报