翻譯的初衷以及爲何選擇《Entity Framework 6 Recipes》來學習,請看本系列開篇html
問題web
你有這樣一張數據庫表,它包含一些額外的信息,這些信息來到一張公共的表。你想使用Table per Type Inheritance(TPT)繼承映射建模。數據庫
解決方案編程
假設你有兩張表與一張公共的表密切相關,如圖2-17所示,Businiss表與eCommerce表、Retail表有1:0...1關係。最關鍵的是,eCommerce表和Retail表中有關於Business表中表明業務的額外的信息。(BusinessID).app
圖2-17 密切相關的表框架
表Retail和eCommerce與表Business密切相關,它們包含一些與業務密切相關的屬性。 按下面的步驟,使用TPT爲這個繼承(Retail和eCommerce實體繼承自Business實體)建模。函數
一、在你的項目中建立一個繼承自DbContext的上下文對象EF6RecipesContext;工具
二、使用代碼清單2-16建立一個POCO實體Business;性能
代碼清單2-16 建立一個POCO實體Business學習
1 [Table("Business", Schema = "Chapter2")] 2 public class Business { 3 [Key] 4 [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 5 public int BusinessId { get; protected set; } 6 public string Name { get; set; } 7 public string LicenseNumber { get; set; } 8 }
三、使用代碼清單2-17 建立一個POCO實體eCommerce,它繼承至Business類
代碼清單2-17 建立一個POCO實體eCommerce
1 [Table("eCommerce", Schema = "Chapter2")] 2 public class eCommerce : Business { 3 public string URL { get; set; } 4 }
四、使用代碼清單2-18 建立一個POCO實體Retail,它繼承至Business類
代碼清單2-18 建立一個POCO實體Retail
1 [Table("Retail", Schema = "Chapter2")] 2 public class Retail : Business { 3 public string Address { get; set; } 4 public string City { get; set; } 5 public string State { get; set; } 6 public string ZIPCode { get; set; } 7 }
5.在上下文對象EF6RecipesContext中添加DbSet<Businss>屬性; (譯註:這裏若是不加構造函數,數據將會保存到默認的數據庫實例SQLExpress中,致使很多讀者認爲數據沒有保存到數據庫表中,構造函數以下代碼清單2-18-1所示)
1 public Recipe8Context() 2 : base("name=EF6CodeFirstRecipesContext") 3 { 4 5 }
原理
表Retail和eCommerce位於,與表Business關係1:0...1的 0...1這一邊。這意味着,一個business能夠沒有額外信息,也能夠有額外的Retail和eCommerce信息。在面向對象的編程中,咱們有一個基類Business,兩個派生類,Retail和eCommerce.
由於1:0...1的關係,在表Retail和eCommerce中,不可能有在表Business中沒有與之相應的行的數據(行)。在面向對象中,派生類繼承基類的屬性,這是繼承的核心。在table per type(常稱爲TPT)映射中,每一個派生類都被表示成一個單獨的表。
代碼清單2-19 演示從模型中插入和獲取
代碼清單2-19 在TPT模型中插入和獲取實體
1 using (var context = new EF6RecipesContext()) { 2 var business = new Business { 3 Name = "Corner Dry Cleaning", 4 LicenseNumber = "100x1" 5 }; 6 context.Businesses.Add(business); 7 var retail = new Retail { 8 Name = "Shop and Save", 9 LicenseNumber = "200C", 10 Address = "101 Main", 11 City = "Anytown", 12 State = "TX", 13 ZIPCode = "76106" 14 }; 15 context.Businesses.Add(retail); 16 var web = new eCommerce { 17 Name = "BuyNow.com", 18 LicenseNumber = "300AB", 19 URL = "www.buynow.com" 20 }; 21 context.Businesses.Add(web); 22 context.SaveChanges(); 23 } 24 using (var context = new EF6RecipesContext()) { 25 Console.WriteLine("\n--- All Businesses ---"); 26 foreach (var b in context.Businesses) { 27 Console.WriteLine("{0} (#{1})", b.Name, b.LicenseNumber); 28 } 29 Console.WriteLine("\n--- Retail Businesses ---"); 30 foreach (var r in context.Businesses.OfType<Retail>()) { 31 Console.WriteLine("{0} (#{1})", r.Name, r.LicenseNumber); 32 Console.WriteLine("{0}", r.Address); 33 Console.WriteLine("{0}, {1} {2}", r.City, r.State, r.ZIPCode); 34 } 35 Console.WriteLine("\n--- eCommerce Businesses ---"); 36 foreach (var e in context.Businesses.OfType<eCommerce>()) { 37 Console.WriteLine("{0} (#{1})", e.Name, e.LicenseNumber); 38 Console.WriteLine("Online address is: {0}", e.URL); 39 } 40 }
代碼清單2-19 建立並初始化Business實體實例和兩個派生類實例。使用上下文中Business實體集中的Add()方法將他們添加到上下文中。
在查詢中,咱們迭代上下文中的Businesses實體集訪問全部的businesses。對於派生類型,咱們使用泛型方法OfType<T>()並經過指定具體的類型參數在實體集中過濾。
代碼清單2-19的輸出以下:
--- All Businesses ---Corner Dry Cleaning (#100X1)
Shop and Save (#200C)
BuyNow.com(#300AB)
--- Retail Businesses ---Shop and Save (#200C)
101 Main
Anytown, TX 76106
---- eCommerce Businesses ---BuyNow.com(#300AB)
Online address is: www.buynow.com
TPT是實體框架支持三個繼承映射中的一個,另外兩個分別是Table per Hierarchy(TPH,將在本章後面部分討論),和Table per Concrete Type(TPC,見第15章)。
TPT繼承映射提供了數據庫方面的靈活性,做爲開發人員,咱們能夠很容易在模型中爲新加的表添加派生類型。可是,每一個派生類型都會涉及一個額外的join鏈接,這會下降系統的性能。 在真實的應用中,咱們已經看到當派生類型不少時,使用TPT繼承映射所帶來的性能問題。
Table per hierarchy(TPH)繼承映射,將在2-10小節講述。它將整個繼承類型存儲在一張單獨的表中,他解決了TPT中的join鏈接問題,並帶來了好的性能。但犧牲了數據庫的靈活性。
Table per concrete(TPC)繼承映射,它被實體框架於運行時所支持,但不被設計器支持。關於TPC的重要應用,請見第15章。
問題
你想在實體類型上建立一個固定的條件來映射表中行的一個子集。
解決方案
假設你有一張包含帳戶信息的數據庫表,如圖2-18所示,該表有一個可爲空的列DeletedOn,它保存着帳戶被刪除的日期和時間。若是帳戶是激活的,列DeletedOn的值爲null,你但願實體類型只表明激活的帳戶,也就是說,是DeletedOn沒有值的帳戶。
圖2-18 有DeletedOn列表Account表
按下面的步驟,爲表Account建模,模型中實體類型只表明激活狀態的帳戶
一、右鍵你的項目,選擇Add(增長) ➤New Item(新建項),而後選擇Visual C#條目下的Data模板下的ADO.NET Entity Data Model(ADO.NET實體數據模型)。
二、選擇Generate from database 從一個已存在的數據庫建立模型,點擊Next(下一步)。
三、能夠選擇一個已存在的數據庫鏈接,也能夠選擇新建一個數據庫鏈接。
四、在選擇數據庫窗口,選擇表Account。後勾選上肯定所生成對象名稱的單複數形式、在模型中包含外鍵列複選框。點擊Finish(完成)
五、單擊Account實體並查看映射詳細信息窗口,若是映射詳細信息窗口未顯示。選擇工具菜單View(視圖) ➤Other Windows(其它窗口) ➤Entity Data Model Mapping Details(實體數據模型映射詳細信息)。在映射詳細信息窗口中單擊 Add a Condition(增長一個條件),而後選擇列DeletedOn, 在Operator(操做符)列中,選擇Is,在Value/Property(值或屬性)列中選擇Null. 這樣就建立了一個映射條件(如圖2-18)
六、右鍵DeleltedOn屬性並選擇Delete,由於咱們使用列DeletedOn做爲條件映射,咱們不把它映射到實體中的屬性。 在咱們模型中,它的值老是爲Null。
原理
在示例中,咱們在實體Account中應用Is Null條件來過濾行中DeletedOn列有值的行。代碼清單2-20演示了從Account表插入和獲取數據。
代理清單2-20 從Account插入和獲取數據
1 using (var context = new EF6RecipesContext()) { 2 context.Database.ExecuteSqlCommand(@"insert into chapter2.account 3 (DeletedOn,AccountHolderId) values ('2/10/2009',1728)"); 4 var account = new Account { AccountHolderId = 2320 }; 5 context.Accounts.Add(account); 6 account = new Account { AccountHolderId = 2502 }; 7 context.Accounts.Add(account); 8 account = new Account { AccountHolderId = 2603 }; 9 context.Accounts.Add(account); 10 context.SaveChanges(); 11 } 12 using (var context = new EF6RecipesContext()) { 13 foreach (var account in context.Accounts) { 14 Console.WriteLine("Account Id = {0}", 15 account.AccountHolderId.ToString()); 16 } 17 }
代碼清單2-20,咱們使用傳統方式,調用在上下文對象的Database屬性的方法ExecuteSqlCommand(),向數據庫中插入一行數據。之全部這樣作,是由於咱們須要插入一行DeletedOn列爲非null值的數據。在模型中,Account實體沒有屬性映射此列。實際上,Account實體已不可能被映射到一個有DeletedOn列值的行,而這又剛好是咱們須要用來測試的。
第一部分餘下的代碼,咱們建立並初始化了3個Account實體類型的實例,並經過SaveChanges()方法保存到數據加中。
當咱們查詢數數據庫時,咱們只能獲取到3個經過SaveChanges()方法保存到數據庫中的Account實體類型實例。經過方法ExecuteSqlCommand()方法插入的數據將不被顯示。下面的輸出驗證了這個結論:
Account Id = 2320
Account Id = 2502
Account Id = 2603
這篇就到此結束,下篇咱們開始TPH.
實體框架交流QQ羣: 458326058,歡迎有興趣的朋友加入一塊兒交流
謝謝你們的持續關注,個人博客地址:http://www.cnblogs.com/VolcanoCloud/