本來打算每天都更新的,但是昨天項目上有點事情,也就忘了。跟所有想繼續看的朋友說聲抱歉,我保證以后會每天寫一篇的,請各位放心,不會有始無終的。
相信通過上次的實例,大家已經知道了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);
}
}
通過這些配置,我們可以完全按照我們的業務定義我們的數據庫表以及字段。
我下次的筆記將介紹如何讓我們的數據庫映射配置更整潔,更容易維護以及一些高級的數據表列屬性的配置。