《Entity Framework 6 Recipes》中文翻譯系列 (8) -----第二章 實體數據建模基礎之繼承關係映射TPT

翻譯的初衷以及爲何選擇《Entity Framework 6 Recipes》來學習,請看本系列開篇html

2-8 Table per Type Inheritance 建模

問題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-9 使用條件過濾對象集

問題

  你想在實體類型上建立一個固定的條件來映射表中行的一個子集。

解決方案

  假設你有一張包含帳戶信息的數據庫表,如圖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/

相關文章
相關標籤/搜索