上一次的日記中我們詳細討論了Entity Framework Code First如何建立表之間的一對多關系。這次的日記中我們將詳細介紹Entity Framework Code First建立多對多關系的默認行為,以及如何通過Fluent API改變默認行為。
本次日記主要介紹一下內容:
1.Entity Framework Code First在什么情況下會建立表之間的多對多關系,以及建立多對多關系時的默認配置。
2.如何通過Fluent API更改Entity Framework Code First建立多對多關系時的默認配置。
1.Entity Framework Code First在什么情況下會建立表之間的多對多關系,以及建立多對多關系時的默認配置。
假設我們的業務領域再次發生了改變,客戶要求我們記錄每種產品的打折信息。根據我們和客戶的交談,我們可以得到如下的業務邏輯:
每個產品種類需要保留所有和它相關的打折促銷記錄。每次打折促銷都有固定的開始時間,結束時間,促銷的產品列表以及打折的折扣率。
我們根據這個業務領域的變化重新改寫了我們的ProductCatalog類:
public class ProductCatalog { public int ProductCatalogId { get; set; } public string CatalogName { get; set; } public string Manufactory { get; set; } public decimal ListPrice { get; set; } public decimal NetPrice { get; set; } public List<Product> ProductInStock { get; set; } public List<SalesPromotion> SalesPromotionHistory { get; set; } public ProductCatalog() { SalesPromotionHistory = new List<SalesPromotion>(); ProductInStock = new List<Product>(); } public Product GetProductInStock() { if (ProductInStock.Count <= 0) { throw new Exception("No product in stock"); } Product product = ProductInStock[0]; ProductInStock.RemoveAt(0); return product; } public void PurchaseProduct(List<Product> products) { ProductInStock.AddRange(products); } public void PurchaseProduct(List<Product> products,decimal newListPrice,decimal newNetPrice) { ProductInStock.AddRange(products); ListPrice = newListPrice; NetPrice = newNetPrice; } public decimal GetPromotionPrice() { if (SalesPromotionHistory.Count <= 0) { return ListPrice; } decimal listPrice = 0; foreach (var promotion in SalesPromotionHistory) { if (promotion.StartDate <= DateTime.Now && promotion.EndDate >= DateTime.Now) { listPrice = ListPrice * promotion.SalesDiscount; } } return listPrice; } }
我們也根據業務領域定義了我們的SalesPromotion類
public class SalesPromotion { public int SalesPromotionId { get; set; } public DateTime StartDate { get; set; } public DateTime EndDate { get; set; } public decimal SalesDiscount { get; set; } public List<ProductCatalog> PromotionProductCatalog { get; set; } public SalesPromotion() { PromotionProductCatalog = new List<ProductCatalog>(); } public void AddProductCatalogToPromotion(ProductCatalog catalog) { catalog.SalesPromotionHistory.Add(this); PromotionProductCatalog.Add(catalog); } }
我們可以寫一次單元測試方法,測試一下Entity Framework Code First會根據類的定義建立出怎樣的數據表關系。
[TestMethod] public void CanAddSalesPromotion() { OrderSystemContext unitOfWork = new OrderSystemContext(); ProductRepository repository = new ProductRepository(unitOfWork); ProductCatalog catalog = repository.GetProductCatalogById(1); SalesPromotion promotion = new SalesPromotion { StartDate = DateTime.Parse("2013-1-18"), EndDate = DateTime.Parse("2013-1-25"), SalesDiscount = 0.75M }; promotion.AddProductCatalogToPromotion(catalog); unitOfWork.CommitChanges(); }
我們打開數據庫看一下,看看Entity Framework Code First從類的定義映射出來的數據庫結構是怎樣的?
大家可以看到由於我們的SalesPromotion和ProductCatalog類中都包含對方類的實例集合,在這種情況下,Entity Framework Code First默認地會將類之間的引用關系映射為數據庫表之間的多對多關系。Entity Framework會建立一個多對多的連接表,表的名字是兩個關聯表的名字加在一起,然后按照它自己的規則去給你加上復數形式。表中的兩個字段,既是引用兩個關聯表的外鍵,並且也作為這個新的連接表的聯合主鍵。
但是大家可能會發現連接表的名字和連接表中字段的名字都是Entity Framework Code First按照自己的規則定義的,我們可以對它們進行修改。
2.如何通過Fluent API更改Entity Framework Code First建立多對多關系時的默認配置
我們可以通過上一次日記我們介紹的Has和With方法去配置連接表的名字和連接表中兩個外鍵列的名字。
public class SalesPromotionEntityConfiguration:EntityTypeConfiguration<SalesPromotion> { public SalesPromotionEntityConfiguration() { Property(p => p.SalesDiscount).HasPrecision(18, 4); HasMany(p => p.PromotionProductCatalog) .WithMany(ca => ca.SalesPromotionHistory) .Map(r => { r.ToTable("ProductCatalogSalesPromotion"); r.MapLeftKey("PromotionId"); r.MapRightKey("CatalogId"); } ); } }
由於我們是對多對多關系產生的數據庫結構進行配置,所以我們需要使用HasMany WithMany。然后我們通過Map方法,可以指定多對多連接中連接表的名字以及連接表中兩個聯合主鍵即外鍵的名字。
我們重新執行我們的單元測試程序,我們可以發現新建的連接表和列的名字都是我們設置的名字。
下一篇日記是Entity Framework Code First映射數據表之間關系的最后一篇:一對一關系。