《Entity Framework 6 Recipes》中文翻譯系列 (25) ------ 第五章 加載實體和導航屬性之加載完整的對象圖和派生類型上的導航屬性

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

5-5  加載完整的對象圖

問題數據庫

  你有一個包含許多關聯實體的模型,你想在一次查詢中,加載完整的對象圖實例。通常地,當一個頁面視圖須要呈現關聯實體集時,你會選擇這種方法,而不是延遲加載,由於延遲加載是經過一系列的短小查詢來獲取關聯實體的。框架

解決方案性能

  假設你有如圖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()方法,確保它不會給你的應用帶來潛在的性能問題。

 

5-6  加載派生類型上的導航屬性

問題

  你有這樣一個模型,有一個或多個派生類,它們在一個或多個實體的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/

相關文章
相關標籤/搜索