我在上一篇日記的最后已經預告了,我這次將給大家介紹一下如何將Fluent API的配置組織的更好,更利於維護,但是一位“特別”同志迫不及待地揭曉了謎底,那么就讓我們來看一下Fluent API的另一種使用方式吧。
我們項目中的domain中一般都有很多的類,如果我們把所有類的代碼都寫在DbContext子類的OnModelCreating重載方法中,那么這個方法將會非常龐大,並且各個類的配置都混雜在一起,非常不利於項目的后期維護。所以我們就需要更好地組織Fluent API的數據映射配置。
從上一篇日記我們知道modelBuilder的Entity<>泛型方法的返回值是EntityTypeConfiguration<>泛型類。我們可以定義一個繼承自EntityTypeConfiguration<>泛型類的類來定義domain中每個類的數據庫配置,我們在這個自定義類的構造函數中使用我們上次提到的那些方法配置數據庫的映射。
public class CustomerEntityConfiguration:EntityTypeConfiguration<Customer>
{
public CustomerEntityConfiguration()
{
HasKey(c => c.IDCardNumber).Property(c => c.IDCardNumber).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
this.Property(c => c.IDCardNumber).HasMaxLength(20);
this.Property(c => c.CustomerName).IsRequired().HasMaxLength(50);
this.Property(c => c.Gender).IsRequired().HasMaxLength(1);
this.Property(c => c.PhoneNumber).HasMaxLength(20);
}
}
昨天我們已經介紹了怎樣去改變Code First默認的數據庫映射。但是當我們通過Fluent API改變數據庫映射時,Code First會如何處理與新的數據庫映射不匹配的數據庫呢?
通過Code First提供的Database類的SetInitializer方法設定Code First如何根據Fluent API數據庫映射配置初始化數據庫。
每次AppDomain加載的時候會執行SetInitializer指定的初始化方法。
SetInitializer方法的參數可以使以下三個泛型類的對象:
CreateDatabaseIfNotExists<>:只有在沒有數據庫的時候才會根據數據庫連接配置創建新的數據庫。這種配置主要用於production環境,因為你不可能把你現在使用的數據庫刪除掉,那樣會損失重要的數據。你需要讓你的實施人員拿着與Fluent API配置對應的數據庫腳本去更新數據庫。
DropCreateDatabaseIfModelChanges<>:只要Fluent API配置的數據庫映射發生變化或者domain中的model發生變化了,就把以前的數據庫刪除掉,根據新的配置重新建立數據庫。這種方式比較適合開發數據庫,可以減少開發人員的工作量。
DropCreateDatabaseAlways<>:不管數據庫映射或者model是否發生變化,每次都重新刪除並根據配置重建數據庫。這種方式可以適用於一些特殊情況的測試,比如說當每次測試結束之后把所有的測試數據都刪除掉,並且在測試開始前插入一些基礎數據。
一般Database.SetInitializer方法都是在應用程序的入口,比如Global.ascx.cs,Main方法等地方調用的。
但是通過代碼調用設置數據庫的初始化方式並不是很方便,因為每種初始化方式都應用於不同的場合,當我們從開發環境變化到production環境時,肯定會使用不同的初始化方式,比如說從DropCreateDatabaseIfModelChanges變為CreateDatabaseIfNotExists。如果每次變化都要重新改代碼,重新編譯的話,太不方便了。
我們可以通過配置指定數據庫初始化的方式,這樣就可以更靈活的改變我們的初始化方式:
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="DatabaseInitializerForType OrderSystemContext"
value="System.Data.Entity.DropCreateDatabaseIfModelChanges[[OrderSystemContext]], EntityFramework" />
</appSettings>
</configuration>
我們還以自定義數據庫初始化類,通過自定的初始化類,還可以將一些基礎數據在創建數據庫之后插入到數據庫中去。
假設我們在測試環境中測試對Product類的相關操作,我們需要一些ProductCatalog的基礎數據,因為Product中有一個Productcatalog的引用。我們可以定義一個自己的數據庫初始化類,繼承DropCreateDatabaseAlways,讓Code First每次在執行測試之前都刪除掉原來的數據庫並且插入一些ProductCatalog的測試數據。
public class DropCreateOrderDatabaseWithSeedValueAlways : DropCreateDatabaseAlways<OrderSystemContext>
{
protected override void Seed(OrderSystemContext context)
{
context.ProductCatalogs.Add(new ProductCatalog { CatalogName = "DELL E6400", Manufactory = "DELL", ListPrice = 5600, NetPrice = 4300 });
context.ProductCatalogs.Add(new ProductCatalog { CatalogName = "DELL E6410", Manufactory = "DELL", ListPrice = 6500, NetPrice = 5100 });
context.ProductCatalogs.Add(new ProductCatalog { CatalogName = "DELL E6420", Manufactory = "DELL", ListPrice = 7000, NetPrice = 5400 });
}
}
我們在測試類的測試初始化方法中就可以指定Code First使用我們自定義的初始化類進行數據庫的初始化:
Database.SetInitializer(new DropCreateOrderDatabaseWithSeedValueAlways());
然后我們就可以使用初始化時插入的基礎數據進行我們的測試了:
[TestMethod]
public void CanAddProduct()
{
OrderSystemContext unitOfWork = new OrderSystemContext();
ProductRepository repository = new ProductRepository(unitOfWork);
ProductCatalog catalog = repository.SearchProductCatalog(c => c.CatalogName == "DELL E6400", 1, 10)[0];
Product product = new Product { Catalog = catalog, CreateDate = DateTime.Parse("2010-2-10"), ExpireDate = DateTime.Parse("2012-2-10") };
repository.AddNewProduct(product);
unitOfWork.CommitChanges();
}
我們打開SQL Server就可以發現基礎的測試數據了:
PS:接下來的日記將涉及DbContext的一些配置,有興趣劇透的同學可以繼續猜測一下,謎題明天揭曉。

