本来打算每天都更新的,但是昨天项目上有点事情,也就忘了。跟所有想继续看的朋友说声抱歉,我保证以后会每天写一篇的,请各位放心,不会有始无终的。
相信通过上次的实例,大家已经知道了Code First怎样使用默认的约定进行各种数据库的映射。但是我们的类既然是根据domain driven design进行设计的,那么所有的类必然都是根据domain进行设计和开发的。我们不可能要求这些类都去遵守Code First默认的约定,如果我们开发这些类的时候都时刻记得Code First的默认规则,那么Code First也就失去了实际的意义了。综上所述,要使用Code First就必须考虑怎样改变Code First的数据库映射规则。
Code First有两种配置数据库映射的方式,一种是使用数据属性DataAnnotation,另一种是Fluent API.
DataAnnotation的配置方式需要你给定义实体和值对象的类和类中的属性加上与数据库映射相关的配置标签。
比如说我们有一个类,类名叫Customer,按照Code First的默认规则,表明应该叫Customers,但是我们想把表的名称设为CustomerInfo,那么我们怎么通过DataAnnotation让Code First根据我们的要求配置数据表的名字呢?
[Table(“CustomerInfo”)]
public class Customer·
{
…….
}
另外一种配置的方式是使用Fluent API,Code First Fluent API 是在DbContext中定义数据库配置的一种方式。要使用Fluent API 就必须在你自定义的继承自DbContext的类中重载OnModelCreating这个方法。这个方法的签名如下:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
通过modelBuilder这个对象的Entity<>泛型方法来配置你的DbContext中的每个类的数据库映射。
我们可以通过Fluent API 配置数据表的名字:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>().ToTable(“CustomerInfo”)
}
那么在我们的实际项目中,我们应该采用哪种配置方式呢?很显然,如果要想让Code First发挥它应有的功能,即分离domain相关的代码和数据存储相关的代码,那么DataAnnotation这种方式显然是不合适的。相信大家也能从我们上面的实例中看出来。如果在domain相关的Customer类中,加入与数据存储相关的代码不符合单一职责的原则,也不符合我们使用Code First的初衷。
所以我在本实例以及我给公司内部新员工配置时主要介绍的是Fluent API的设置方式,如果大家对DataAnnotation的设计方式有兴趣,可以自己查找相关资料,但是我不推荐大家使用这种配置方式,并且他的功能也不如Fluent API 强大,很多配置只能通过Fluent API 实现。
说了这么多理论,还是先看一下如何根据我们的实际业务逻辑来配置数据表中各column的属性。
假设我们的订单管理系统处理的都是个人客户,假设每个个人客户都是domain中的实体,假设实体的标识符是每个人的身份证号。假设我们的客户都是中国人,那么身份证号应该是18位。客户的名字应该不会太长,中国人的名字一般不会超过十个字吧?性别用M和F标识,我们还记录客户的地址和联系电话。我们假设客户的联系电话记录中国境内客户的手机号,现在的手机号一般都是13位。我们的实体类如下:
public class Customer
{
public string IDCardNumber { get; set; }
public string CustomerName { get; set; }
public string Gender { get; set; }
public string Address { get; set; }
public string PhoneNumber { get; set; }
}
大家应该清楚按照Code First的规则,数据表的主键必须是int,并且名称必须是Id或者类名+Id。我们Customer表默认的主键应该是public int CustomerId{get;set;}。string类型默认会映射为nvarchar(max)
我们怎样根据上述的业务逻辑改变数据表中列的属性呢?
modelBuilder的Entity<Customer>方法的返回值是EntityTypeConfiguration<Customer>类,可以通过它的Property方法,并且通过lamda表达式设置数据表中指定列的属性。
我们现在可以看看怎样通过一些设定属性的方法来配置我们的数据表的列。
IsRequired():通过这个方法指定该列是not-null的。
HasMaxLength():设定nvarchar列的最大字符数。
HasPercision(percison,scale):设定decimal列的最大值和小数点后位数。
HasColumnType(“TypeName”):设定列的类型,但是指定的列的类型必须与类中的属性的类型相兼容。这个兼容规则后面将详细介绍。
HasDatabaseGeneratedOption(DatabaseGeneratedOption):指定列是否是自增长列。
DatabaseGeneratedOption有三个选项:Idnetity:自增长
None:非自增长
Computed:用于一些通过计算得到值的列。
modelBuilder的Entity<Customer>方法的返回值还有一个HasKey方法用于设置数据表的主键。
通过这些方法我们就可以根据我们的业务需要来定义我们的数据表了:
public class OrderSystemContext:DbContext
{
public DbSet<Customer> Customers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{ modelBuilder.Entity<Customer>().HasKey(c => c.IDCardNumber).Property(c => c.IDCardNumber).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<Customer>().Property(c => c.IDCardNumber).HasMaxLength(20);
modelBuilder.Entity<Customer>().Property(c => c.CustomerName).IsRequired().HasMaxLength(50);
modelBuilder.Entity<Customer>().Property(c => c.Gender).IsRequired().HasMaxLength(1);
modelBuilder.Entity<Customer>().Property(c => c.PhoneNumber).HasMaxLength(20);
}
}
通过这些配置,我们可以完全按照我们的业务定义我们的数据库表以及字段。
我下次的笔记将介绍如何让我们的数据库映射配置更整洁,更容易维护以及一些高级的数据表列属性的配置。