一. 簡介
上一個章節中,也介紹了立即加載和延遲加載,但上一個章節是針對單表而言的,不含外鍵,立即也好,延遲也好,都是指單表中的數據。但本章節重點介紹的三種加載方式均是針對含(導航屬性、外鍵)的情況下,查詢主表,從表中的數據加載情況。
下面准備兩張表:Student和StudentAddress兩張表,一對一 or 零 的關系,實體結構如下,通過CodeFirst來反向生成數據庫。
1 /// <summary> 2 /// 學生表(一個學生只能有一個地址或沒有地址) 3 /// </summary> 4 public class Student 5 { 6 public Student() 7 { 8 9 } 10 public string studentId { get; set; } 11 12 public string studentName { get; set; } 13 14 15 public virtual StudentAddress StudentAddress { get; set; } 16 }
1 /// <summary> 2 /// 學生地址表(一個地址只能對應一個學生) 3 /// </summary> 4 public class StudentAddress 5 { 6 public StudentAddress() 7 { 8 9 } 10 11 [ForeignKey("stu")] 12 //特別注意這個地方,stu對應下面的 Student stu; 13 //另外特別注意:studentAddressId,符合默認的Id生成規則,自動映射成主鍵,否則需要加【key】特性 14 public string studentAddressId { get; set; } 15 16 public string addressName { get; set; } 17 18 public virtual Student stu { get; set; } 19 }
二. Lazy Loading
1. 又名:延遲加載、懶加載
2. 需要滿足的條件:
①:poco類是public且不能為sealed
②:導航屬性需要標記為Virtual
滿足以上兩個條件,EF6默認就為延遲加載的模式。(默認:db.Configuration.LazyLoadingEnabled = true; )
3. 含義:每次調用子實體(外鍵所在的實體)的時候,才去查詢數據庫. 主表數據加載的時候,不去查詢外鍵所在的從表。
4. 關閉延遲加載的辦法: db.Configuration.LazyLoadingEnabled = false;
特別注意:關閉延遲加載后,查詢主表數據時候,主表的中從表實體為null.
1. 延遲加載代碼測試
1 using (dbContext1 db = new dbContext1()) 2 { 3 Console.WriteLine("--------------------------- 01-延遲加載 -----------------------------"); 4 db.Database.Log = Console.WriteLine; 5 6 //EF默認就是延遲加載,默認下面的語句就是true,所以下面語句注釋沒有任何影響 7 //db.Configuration.LazyLoadingEnabled = true; 8 9 var list = db.Student.ToList(); //此處加載的數據,根據監測得出結論,沒有對從表進行任何查詢操作 10 foreach (var stu in list) 11 { 12 Console.WriteLine("學生編號:{0},學生姓名:{1}", stu.studentId, stu.studentName); 13 14 //下面調用導航屬性(一對一的關系) 每次調用時,都要去查詢數據庫(查詢從表) 15 var stuAddress = stu.StudentAddress; 16 Console.WriteLine("地址編號:{0},地址名稱:{1}", stuAddress.studentAddressId, stu.studentName); 17 } 18 }
2. 關閉延遲加載
1 using (dbContext1 db = new dbContext1()) 2 { 3 try 4 { 5 Console.WriteLine("--------------------------- 02-關閉延遲加載 -----------------------------"); 6 db.Database.Log = Console.WriteLine; 7 8 //下面的語句為關閉延遲加載 9 db.Configuration.LazyLoadingEnabled = false; 10 11 var list = db.Student.ToList(); //關閉延遲加載后,此處從表實體StudentAddress為null,后面不會再次查詢了 12 foreach (var stu in list) 13 { 14 Console.WriteLine("學生編號:{0},學生姓名:{1}", stu.studentId, stu.studentName); 15 16 //StudentAddress為null,不會再次查詢數據庫,所以此處報錯 17 var stuAddress = stu.StudentAddress; 18 Console.WriteLine("地址編號:{0},地址名稱:{1}", stuAddress.studentAddressId, stu.studentName); 19 } 20 } 21 catch (Exception ex) 22 { 23 Console.WriteLine(ex.Message); 24 } 25 }
三. Eager Loading
1. 又名:立即加載、貪婪加載、預加載
2. 使用步驟:
①:先關閉延遲加載:db.Configuration.LazyLoadingEnabled = false;
②:查詢主表的同時通過Include把從表數據也查詢出來:
3. 含義:由於查詢主表的時候通過Include已經一次性將數據查詢了出來,所以在調用從表數據的時候,均從緩存中讀取,無須查詢數據庫
代碼測試
1 using (dbContext1 db = new dbContext1()) 2 { 3 Console.WriteLine("--------------------------- 03-立即加載 -----------------------------"); 4 db.Database.Log = Console.WriteLine; 5 6 //1.關閉延遲加載 7 db.Configuration.LazyLoadingEnabled = false; 8 9 //2. 獲取主表數據的同時,通過Include將從表中的數據也全部加載出來 10 var list = db.Student.Include("StudentAddress").ToList(); 11 foreach (var stu in list) 12 { 13 Console.WriteLine("學生編號:{0},學生姓名:{1}", stu.studentId, stu.studentName); 14 15 //這里獲取從表中的數據,均是從緩存中獲取,無需查詢數據庫 16 var stuAddress = stu.StudentAddress; 17 Console.WriteLine("地址編號:{0},地址名稱:{1}", stuAddress.studentAddressId, stu.studentName); 18 } 19 20 }
四. Explicit Loading
1. 又名:顯示加載
2. 背景:關閉延遲加載后,單純查詢主表的數據,后面又想再次查詢從表,這個時候就需要用到顯示加載了.
3. 前提:
①:關閉了延遲加載:db.Configuration.LazyLoadingEnabled = false;
②:單純查詢了主表,沒有使用Include函數關聯查詢從表.
4. 使用步驟:
①:單個實體用:Reference
②:集合用:Collection
③:最后需要Load一下
5. 含義:關閉了延遲加載,單純查詢了主表數據,這個時候需要重新查詢從表數據,就要用到顯式加載了
代碼測試
1 using (dbContext1 db = new dbContext1()) 2 { 3 Console.WriteLine("--------------------------- 04-顯式加載 -----------------------------"); 4 db.Database.Log = Console.WriteLine; 5 6 //1.關閉延遲加載 7 db.Configuration.LazyLoadingEnabled = false; 8 9 //2.此處加載的數據,不含從表中的數據 10 var list = db.Student.ToList(); 11 foreach (var stu in list) 12 { 13 Console.WriteLine("學生編號:{0},學生姓名:{1}", stu.studentId, stu.studentName); 14 15 //3.下面的這句話,可以開啟重新查詢一次數據庫 16 //3.1 單個屬性的情況用Refercence 17 db.Entry<Student>(stu).Reference(c => c.StudentAddress).Load(); 18 //3.2 集合的情況用Collection 19 //db.Entry<Student>(stu).Collection(c => c.StudentAddress).Load(); 20 21 //下面調用導航屬性(一對一的關系) 每次調用時,都要去查詢數據庫 22 var stuAddress = stu.StudentAddress; 23 Console.WriteLine("地址編號:{0},地址名稱:{1}", stuAddress.studentAddressId, stu.studentName); 24 } 25 26 }