問(wèn)題描述:
創(chuàng)新互聯(lián)建站長(zhǎng)期為上千多家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開(kāi)放共贏平臺(tái),與合作伙伴共同營(yíng)造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為石林企業(yè)提供專業(yè)的成都網(wǎng)站建設(shè)、網(wǎng)站制作,石林網(wǎng)站改版等技術(shù)服務(wù)。擁有十年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開(kāi)發(fā)。
假設(shè)有一些表用于保存公共表的額外信息,現(xiàn)在想使用TPT繼承對(duì)其建模。
解決方案:
假定現(xiàn)在有2個(gè)表關(guān)聯(lián)到一個(gè)公共表,如下圖所示:
Business表和Retail表及eCommerce表間都是一個(gè)1對(duì)零個(gè)或1個(gè)的關(guān)系,其中Business表是1端。關(guān)鍵的特點(diǎn)是Retail表和eCommerce表擴(kuò)展了Business表中business的信息。
Business表包含一些任何business都有的公共屬性,Retail表和eCommerce表都同它緊密相連。為了建模一個(gè)TPT繼承,也就是說(shuō)Retail實(shí)體類(lèi)型和eCommerce實(shí)體類(lèi)型都繼承于Business基實(shí)體類(lèi)型,需要實(shí)現(xiàn)以下步驟:
1、生成一個(gè)派生自DbContext類(lèi)的類(lèi),需要引入System.Data.Entity名稱空間,添加默認(rèn)構(gòu)造函數(shù),并調(diào)用父類(lèi)的構(gòu)造函數(shù)base("name=connectionStringName")。connectionStringName是EF框架的連接字符串名稱,在配置文件中可以找到,沒(méi)有的話需要手動(dòng)添加或使用EDM向?qū)У拇a優(yōu)先方式創(chuàng)建。并重寫(xiě)父類(lèi)的OnModelCreating方法。
2、生成一個(gè)Business POCO 實(shí)體類(lèi)型。
[Table("Business",Schema ="Chapter2")] public class Business { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int BusinessId { get; set; } public string Name { get; set; } public string LicenseNumber { get; set; } }
3、生成一個(gè)eCommerce POCO 實(shí)體類(lèi)型和Retail POCO 實(shí)體類(lèi)型,它們都繼承于Business POCO實(shí)體類(lèi)型。
[Table("eCommerce",Schema ="Chapter2")] public class eCommerce:Business { public string URL { get; set; } } [Table("Retail",Schema ="Chapter2")] public class Retail : Business { public string Address { get; set; } public string City { get; set; } public string State { get; set; } public string ZIPCode { get; set; } }
4、在DbContext子類(lèi)中添加一個(gè)DbSet
原理:
Retail表和eCommerce表是同Business表的1:0..1關(guān)系的0..1端。這意味著一個(gè)business實(shí)體可以沒(méi)有任何額外的信息,也可以有額外的Retail信息或eCommerce信息。在OOP術(shù)語(yǔ)中,描述為一個(gè)基類(lèi)型Business和2個(gè)派生類(lèi)型Retail和eCommerce。
因?yàn)檫@是一個(gè)1:0..1的關(guān)系,所以在Retail表和eCommerce表中不存在這樣的記錄,它們?cè)贐usiness表中不存在相關(guān)的記錄。在面向?qū)ο蟮男g(shù)語(yǔ)中描述為,派生類(lèi)的實(shí)例包含有基類(lèi)的屬性。派生類(lèi)擴(kuò)展基類(lèi)屬性的概念是繼承的關(guān)鍵特性之一。在TPT繼承中,每一個(gè)派生類(lèi)都表示為一個(gè)單獨(dú)的表。
using (var context = new EF6Recipes8Context()) { var business = new Business { Name = "Corner Dry Cleaning", LicenseNumber = "100x1" }; context.Businesses.Add(business); var retail = new Retail { Name = "Shop and Save", LicenseNumber = "200C", Address = "101 Main", City = "AnyTown", State = "TX", ZIPCode = "76106" }; context.Businesses.Add(retail); var web = new eCommerce { Name = "BuyNow.com", LicenseNumber = "300AB", URL = "www.buynow.com" }; context.Businesses.Add(web); context.SaveChanges(); } using (var context = new EF6Recipes8Context()) { Console.WriteLine("\n--- All Business ---"); foreach (var b in context.Businesses) { Console.WriteLine("{0} (#{1})", b.Name, b.LicenseNumber); } Console.WriteLine("\n---Retail Business ---"); foreach (var r in context.Businesses.OfType()) { Console.WriteLine("{0} (#{1}", r.Name, r.LicenseNumber); Console.WriteLine("{0}", r.Address); Console.WriteLine("{0}, {1} {2}", r.City, r.State, r.ZIPCode); } Console.WriteLine("\n--- eCommerce Business ---"); foreach (var e in context.Businesses.OfType ()) { Console.WriteLine("{0} (#{1})", e.Name, e.LicenseNumber); Console.WriteLine("Online address is: {0}", e.URL); } }
上面的代碼生成和實(shí)例化Business實(shí)體類(lèi)型和2個(gè)派生類(lèi)型,并使用context中的Business實(shí)體集合的Add()方法將其添加到context中。
在查詢部分,為了訪問(wèn)所有的business實(shí)例,我們迭代整個(gè)business實(shí)體集合;為了獲取派生類(lèi)型,我們調(diào)用OfType方法過(guò)濾Business實(shí)體集合。
TPT是EF支持的3種繼承模式中的一個(gè)。另外2個(gè)是TPH(Table per Hierarchy)和TPCT(Table per Concrete Type)。
TPT繼承提供了很大的數(shù)據(jù)庫(kù)靈活性,我們能夠容易的添加表作為一個(gè)派生類(lèi)型,并按照它們自己的方式添加到模型中。然而,每一個(gè)派生類(lèi)型涉及額外的連接,這將導(dǎo)致性能下降。在實(shí)際的應(yīng)用中,使用TPT時(shí),我們已經(jīng)看到顯著的性能問(wèn)題當(dāng)一些派生類(lèi)被建模時(shí)。
而TPH存儲(chǔ)整個(gè)繼承在單個(gè)表,它消除了TPT的連接,因此提供更好的性能,但更低的靈活性。
TPCT被EF運(yùn)行時(shí)支持,而不是設(shè)計(jì)時(shí)。