翻譯的初衷以及爲何選擇《Entity Framework 6 Recipes》來學習,請看本系列開篇html
問題數據庫
你有一個包含許多關聯實體的模型,你想在一次查詢中,加載完整的對象圖實例。通常地,當一個頁面視圖須要呈現關聯實體集時,你會選擇這種方法,而不是延遲加載,由於延遲加載是經過一系列的短小查詢來獲取關聯實體的。框架
解決方案性能
假設你有如圖5-20所示的概念模型。每門課程有不少節,每一節由一個老師教多名學生。學習
圖5-20 一個包含許多關聯實體的模型大數據
使用Include()方法和查詢路徑參數,經過一個查詢,從數據庫中獲取全部的課程,課時,老師和學生,如代碼清單5-12所示。優化
1 using (var context = new EFRecipesEntities()) 2 { 3 var course = new Course {Title = "Biology 101"}; 4 var fred = new Instructor {Name = "Fred Jones"}; 5 var julia = new Instructor {Name = "Julia Canfield"}; 6 var section1 = new Section {Course = course, Instructor = fred}; 7 var section2 = new Section {Course = course, Instructor = julia}; 8 9 var jim = new Student {Name = "Jim Roberts"}; 10 jim.Sections.Add(section1); 11 12 var jerry = new Student {Name = "Jerry Jones"}; 13 jerry.Sections.Add(section2); 14 15 var susan = new Student {Name = "Susan O'Reilly"}; 16 susan.Sections.Add(section1); 17 18 var cathy = new Student {Name = "Cathy Ryan"}; 19 cathy.Sections.Add(section2); 20 21 course.Sections.Add(section1); 22 course.Sections.Add(section2); 23 24 context.Students.Add(jim); 25 context.Students.Add(jerry); 26 context.Students.Add(susan); 27 context.Students.Add(cathy); 28 29 context.Courses.Add(course); 30 context.SaveChanges(); 31 } 32 33 //字符串查詢路徑參數 34 using (var context = new EFRecipesEntities()) 35 { 36 var graph = context.Courses 37 .Include("Sections.Instructor") 38 .Include("Sections.Students"); 39 Console.WriteLine("Courses"); 40 Console.WriteLine("======="); 41 42 foreach (var course in graph) 43 { 44 Console.WriteLine("{0}", course.Title); 45 foreach (var section in course.Sections) 46 { 47 Console.WriteLine("\tSection: {0}, Instrutor: {1}", section.SectionId, section.Instructor.Name); 48 Console.WriteLine("\tStudents:"); 49 foreach (var student in section.Students) 50 { 51 Console.WriteLine("\t\t{0}", student.Name); 52 } 53 Console.WriteLine("\n"); 54 } 55 } 56 } 57 58 // 強類型查詢路徑參數 59 using (var context = new EFRecipesEntities()) 60 { 61 var graph = context.Courses 62 .Include(x => x.Sections.Select(y => y.Instructor)) 63 .Include(x => x.Sections.Select(z => z.Students)); 64 65 Console.WriteLine("Courses"); 66 Console.WriteLine("======="); 67 68 foreach (var course in graph) 69 { 70 Console.WriteLine("{0}", course.Title); 71 foreach (var section in course.Sections) 72 { 73 Console.WriteLine("\tSection: {0}, Instrutor: {1}", section.SectionId, section.Instructor.Name); 74 Console.WriteLine("\tStudents:"); 75 foreach (var student in section.Students) 76 { 77 Console.WriteLine("\t\t{0}", student.Name); 78 } 79 Console.WriteLine("\n"); 80 } 81 } 82 } 83 84 Console.WriteLine("Press <enter> to continue..."); 85 Console.ReadLine();
代碼清單5-12的輸出以下:spa
Courses Courses ======= Biology 101 Section: 19, Instructor: Fred Jones Students: Jim Roberts Susan O'Reilly Section: 20, Instructor: Julia Canfield Students: Jerry Jones Cathy Ryan
原理翻譯
分別給Include()方法傳遞字符串和強類型形式的查詢路徑參數。查詢路徑表明咱們想使用Include()方法加載的整個對象圖路徑。Include()方法,擴展查詢包含查詢路徑中指定的實體對象。code
在代碼清單5-12中,咱們最早演示基於字符串形式參數的Include()方法。Include()方法第一次調用,經過表示對象圖部分對象的查詢路徑將Secion擴展至Instructor。這使得查詢包含了全部的課時(Sections)和它們的教師(Instructors)。而後在第一個Include()方法後連接另外一個Include()方法,它包含一個將Secton擴展到Student的查詢路徑,這將使得查詢包含全部課時(Sections)和它們的學生(Students)。最終結果就是,實例化了一個完整的對象圖,它包含全部的Course實體和在模型中每一個與它關聯的實體。
在代碼的第二部分,咱們演示了使用強類型查詢路徑參數的Include()方法,注意這兩個Include()方法是如何結合一個參數,x.Sections,並使用Select()方法整合關聯實體Instructor和Students的。
注意 接受強類型參數的重載方法Include(),是在命名空間System.Data.Entity中公佈的擴展方法。要使用這個方法,你須要在你的類中使用using引用這個命名空間。
你可使用導航屬性構造任何深度的查詢路徑,這給你加載部分或是完整的對象圖帶來很大的靈活性。實體框架會經過修剪掉重疊和重複的查詢路徑來優化查詢。
Include()方法的語法和主義都看似簡單。但不要被這種簡單迷惑了,覺得使用Include()方法是不須要付出性能代價的。調用多個Includ()方法預先加載數據,會迅速地增長生成的SQL語句的複雜性,同時,從數據庫中返回的數據量也急劇上升。複雜的查詢語句會致使產生低性能的查詢計劃,大數據量的返回,也會讓實體框架花費大量的時間來移除重複的數據。因此你應該明智地使用Include()方法,確保它不會給你的應用帶來潛在的性能問題。
問題
你有這樣一個模型,有一個或多個派生類,它們在一個或多個實體的Has-a關係中(一個對象是另外一個對象的一部分)。你想在一次數據庫交互中預先加載全部關聯的實體。
解決方案
假設你有如圖5-21所示的模型。
圖5-21 一個包含 Plumbers(管道工人)、他們的JobSite(工做場所)和其它關聯實體的模型
在模型中,管道工人(Plumber)實體繼承至手藝人(Tradesman)實體,一個管道工人(Plumber)有一個工做場所(JobSite),這是經過一個一對多關聯表示的。工做場所(JobSite)類型繼承至地點(Location)實體。Location有一個Phone,這是經過一個一對多關聯表示的。最後,一個JobSite可能擁有0或者多個工頭(Formen),也是經過一個一對多關聯表示的。
假設你想獲取一個管道工人,他的工做場所,工做場所的電話,以及工做場所所有的工頭。你想在一次數據庫交互中返回這些數據。
代碼清單5-13演示,在一個查詢中經過使用Includ()方法預先加載關聯實體。
代碼清單5-13.使用Include()方法在一次數據庫交互中預先加載關聯實體
1 using (var context = new EFRecipesEntities()) 2 { 3 var foreman1 = new Foreman {Name = "Carl Ramsey"}; 4 var foreman2 = new Foreman {Name = "Nancy Ortega"}; 5 var phone = new Phone {Number = "817 867-5309"}; 6 var jobsite = new JobSite 7 { 8 JobSiteName = "City Arena", 9 Address = "123 Main", 10 City = "Anytown", 11 State = "TX", 12 ZIPCode = "76082", 13 Phone = phone 14 }; 15 jobsite.Foremen.Add(foreman1); 16 jobsite.Foremen.Add(foreman2); 17 var plumber = new Plumber {Name = "Jill Nichols", Email = "JNichols@plumbers.com", JobSite = jobsite}; 18 context.Tradesmen.Add(plumber); 19 context.SaveChanges(); 20 } 21 22 using (var context = new EFRecipesEntities()) 23 { 24 var plumber = 25 context.Tradesmen.OfType<Plumber>().Include("JobSite.Phone").Include("JobSite.Foremen").First(); 26 Console.WriteLine("Plumber's Name: {0} ({1})", plumber.Name, plumber.Email); 27 Console.WriteLine("Job Site: {0}", plumber.JobSite.JobSiteName); 28 Console.WriteLine("Job Site Phone: {0}", plumber.JobSite.Phone.Number); 29 Console.WriteLine("Job Site Foremen:"); 30 foreach (var boss in plumber.JobSite.Foremen) 31 { 32 Console.WriteLine("\t{0}", boss.Name); 33 } 34 } 35 36 Console.WriteLine("Press <enter> to continue..."); 37 Console.ReadLine();
代碼清單5-13的輸出以下:
Plumber's Name: Jill Job Site: City Arena Job Site Phone: 817 8 Job Site Foremen: Carl Ramsey Nancy Ortega
原理
咱們的查詢以獲取派生類型Plumber開始,爲了獲取它們,咱們使用了OfType<Plumber>()方法,OfType<>()方法,從實體集中獲取給定子類型的實例。
咱們想從Plumber中加載與之關聯的實體JobSite和與JobSite關聯的實體Phone。注意JobSite實體並無導航屬性Phone。可是JobSite派生至Location,Location有導航屬性Phone。由於Phone是基類屬性,因此派生類一樣能使用,這就是使用繼承的好處。它使查詢路徑能夠簡單地表示爲:JobSite.Phone。
而後,我再次使用查詢路徑和Include()方法,從JobSite實體中獲取Foreman實體.JobSite和Foreman有一個一對多關聯。注意導航屬性的複數形式(從Foreman變成Foremen)。
最後,咱們使用First()方法選擇第一個Plumber實體,這樣將返回一個Plumber類型,而不是一個Plumber對象集合。
生成的查詢有點複雜,涉及了幾個Join鏈接和子查詢。與此對應的,使用延遲加載,將會須要屢次數據庫交互,這樣會帶來性能損失。特別是加載多個Plumbers時。
實體框架交流QQ羣: 458326058,歡迎有興趣的朋友加入一塊兒交流
謝謝你們的持續關注,個人博客地址:http://www.cnblogs.com/VolcanoCloud/