Code First Entity Framework 6化被動爲主動之explicit loading模式實戰分析( 附源碼)

在使用Entity Framework加載關聯實體時,能夠有三種方式:html

1.懶加載(lazy Loading);數據庫

 2.貪婪加載(eager loading);  ide

3.顯示加載(explicit loading)。函數

EF默認使用的是懶加載(lazy Loading)。一切由EF自動處理。性能

這種方式會致使應用程序屢次鏈接數據庫,這種狀況推薦在數據量較大的狀況下使用。當咱們須要加載數據較少時,一次性所有加載數據會相對更高效。測試

咱們來看看EF的顯示加載(explicit loading)如何讓咱們徹底掌控數據的加載(非自動化)。優化

這裏咱們使用Code First模式。ui

數據表:商品表,商品分類表,品牌表spa

Step1:新建控制檯應用程序,添加EF6引用(可使用NuGet獲取)code

 

 

Step2:建立三個實體對象:Product、Category 、Brand

Product實體類:

複製代碼
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 
 6 namespace EFExplicitLoading.Models {
 7   public  class Product {
 8       public int ProductId { get; set; }
 9       public String Title { get; set; }
10       public int CategoryId { get; set; }
11 
12       public virtual Category Category { get; set; }
13 
14       public virtual Brand Brand { get; set; }
15 
16   }
17 }
複製代碼

 

 

Category實體類:

複製代碼
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 
 6 namespace EFExplicitLoading.Models {
 7   public  class Category {
 8 
 9       public Category() {
10           Products = new HashSet<Product>();
11       }
12 
13       public int CategoryId { get; set; }
14       public String Name { get; set; }
15 
16       public virtual ICollection<Product> Products { get; set; } 
17   }
18 }
複製代碼

 

 

 

Brand實體類:

複製代碼
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 
 6 namespace EFExplicitLoading.Models {
 7   public  class Brand {
 8 
 9       public Brand() {
10           Products = new HashSet<Product>();
11       }
12 
13       public int BrandId { get; set; }
14 
15       public String Name { get; set; }
16 
17       public virtual ICollection<Product> Products { get; set; } 
18   }
19 }
複製代碼

 

Step3:建立派生自DbContext的類(ExplicitContext)

複製代碼
 1 using System.Data.Entity;
 2 using EFExplicitLoading.Models;
 3 
 4 namespace EFExplicitLoading {
 5 
 6     public class ExplicitContext : DbContext {
 7         public ExplicitContext()
 8             : base("ExplicitConnectionString") {
 9         }
10 
11         public DbSet<Product> Products { get; set; }
12 
13         public DbSet<Category> Categories { get; set; }
14 
15         public DbSet<Brand> Brands { get; set; }
16 
17         protected override void OnModelCreating(DbModelBuilder modelBuilder) {
18             modelBuilder.Entity<Product>().ToTable("Product");
19             modelBuilder.Entity<Brand>().ToTable("Brand");
20             modelBuilder.Entity<Category>().ToTable("Category");
21         }
22     }
23 
24 }
複製代碼

 

Step4:在App.config中添加connectionStrings節點

1   <connectionStrings>
2     <add name="ExplicitConnectionString" connectionString="Data Source=.;Initial Catalog=EFExplicit;Integrated Security=True;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" />
3   </connectionStrings>

 

準備工做已就緒,當咱們使用ExplicitContext類時,EF會根據實體自動建立數據庫和表

 

下面回到控制檯入口函數處:

Step5:添加測試數據

複製代碼
 1 
 2             using (var context = new ExplicitContext()) {
 3                 //先清空數據
 4                 context.Database.ExecuteSqlCommand("delete from product");
 5                 context.Database.ExecuteSqlCommand("delete from brand");
 6                 context.Database.ExecuteSqlCommand("delete from category");
 7 
 8                 var category1 = new Category {Name = "服裝"};
 9                 var category2 = new Category {Name = "家電"};
10                 var brand1 = new Brand {Name = "品牌1"};
11                 var brand2 = new Brand {Name = "品牌2"};
12 
13                 context.Products.Add(new Product {Brand = brand1, Category = category1, Title = "產品1"});
14                 context.Products.Add(new Product {Brand = brand1, Category = category2, Title = "產品2"});
15                 context.Products.Add(new Product {Brand = brand1, Category = category2, Title = "產品3"});
16                 context.Products.Add(new Product {Brand = brand2, Category = category2, Title = "產品4"});
17                 context.Products.Add(new Product {Brand = brand2, Category = category2, Title = "產品5"});
18                 context.Products.Add(new Product {Brand = brand2, Category = category2, Title = "產品6"});
19                 context.SaveChanges();
20             }
複製代碼

 

 

Step6:開始測試explicit loading

複製代碼
 1 using (var context = new ExplicitContext()) {
 2                 //禁用懶加載
 3                 context.Configuration.LazyLoadingEnabled = false;
 4 
 5                 var category = context.Categories.First(c => c.Name == "家電");
 6 
 7                 //判斷分類關聯的產品是否已經被加載
 8                 if (!context.Entry(category).Collection(x => x.Products).IsLoaded) {
 9                     context.Entry(category).Collection(x => x.Products).Load();
10                     Console.WriteLine("分類{0}的產品被顯示加載...", category.Name);
11                 }
12 
13                 Console.Write("分類{0}下的共有{1}件產品", category.Name, category.Products.Count());
14 
15                 foreach (var product in context.Products) {
16                     if (!context.Entry(product).Reference(x => x.Category).IsLoaded) {
17                         context.Entry(product).Reference(x => x.Category).Load();
18                         Console.WriteLine("分類{0}被顯示加載.", product.Category.Name);
19                     }
20                     else {
21                         Console.WriteLine("分類{0}已經加載過了.", product.Category.Name);
22                     }
23                 }
24 
25                 //從新使用category計算 產品數量
26                 Console.WriteLine("產品加載完後,分類{0}下有{1}件產品", category.Name, category.Products.Count());
27 
28                 category.Products.Clear();
29                 Console.WriteLine("產品集合被清空了.");
30                 Console.WriteLine("此時分類{0}下有{1}件產品", category.Name, category.Products.Count());
31 
32 
33                 context.Entry(category).Collection(x => x.Products).Load();
34                 Console.WriteLine("從新顯示加載產品");
35                 Console.WriteLine("此時分類{0}下有{1}件產品", category.Name, category.Products.Count());
36 
37                 //使用ObjectContext刷新實體
38                 var objectContext = ((IObjectContextAdapter) context).ObjectContext;
39                 var objectSet = objectContext.CreateObjectSet<Product>();
40                 objectSet.MergeOption = MergeOption.OverwriteChanges;
41                 objectSet.Load();
42                 //MergeOption.AppendOnly
43                 //MergeOption.OverwriteChanges
44                 //MergeOption.NoTracking
45                 //MergeOption.PreserveChanges
46                 
47 
48                 Console.WriteLine("產品集合以MergeOption.OverwriteChanges的方式從新加載");
49                 Console.WriteLine("此時分類{0}下有{1}件產品", category.Name, category.Products.Count());
50             }
51 
52 
53             Console.WriteLine("測試部分加載...");
54             //測試部分加載,而後加載全部
55             using (var context = new ExplicitContext()) {
56                 //禁用懶加載
57                 context.Configuration.LazyLoadingEnabled = false;
58                 var category = context.Categories.First(c => c.Name == "家電");
59                 //先加載1條數據
60                 Console.WriteLine("先加載1條數據");
61                 context.Entry(category).Collection(x => x.Products).Query().Take(1).Load();
62                 Console.WriteLine("分類{0}下共有{1}件產品", category.Name, category.Products.Count());
63 
64                 //加載全部
65                 context.Entry(category).Collection(x => x.Products).Load();
66                 Console.WriteLine("加載全部數據");
67 
68                 Console.WriteLine("此時分類{0}下有{1}件產品", category.Name, category.Products.Count());
69             }
70 
71             Console.ReadKey();
複製代碼

 

 

執行結果:

  禁用懶加載(lazy loading)            

要想測試咱們的explict loading,必須先禁用懶加載,有2種方式能夠禁用懶加載:

1.設置Context.Configuration.LazyLoadingEnabled的值爲false   或

2.移除每個實體類中的關聯實體屬性(導航屬性)的virtual訪問修飾符,這種方法能夠更精確控制懶加載,即只禁用移除virtual修飾符實體的懶加載

 

  IsLoaded屬性和Load方法            

接下來,咱們利用EF獲取一條Name爲家電的分類實體數據,因爲Category與Product是一對多的關係,此時咱們可使用IsLoaded屬性來測試此分類下的Products集合是否被加載

context.Entry(category).Collection(x => x.Products).IsLoaded

若沒有加載,咱們使用Load方法來加載關聯的Products,在接下來的foreach遍歷中,咱們能夠看到,Category關聯的Product已經所有被加載完畢

PS:當使用Take(n)方法Load數據時,IsLoaded屬性依然爲False,由於只有當關聯的全部數據加載完,IsLoaded才爲true

  關係修復(relationship fixup)      

這裏Entity Framework使用稱爲"關係修復"的技術(relationship fixup),即當咱們單獨加載關聯實體的時候(Product),這些數據會自動掛載到已經載入的實體(Category),但這種關係修復並非老是會生效,好比在多對多的關係中就不會這樣處理。

  關於Clear()                                 

而後咱們打印出來Category中有多少Product,以後使用Clear()方法清空category對象的關聯集合Products。

Clear方法會移除Category和Product的關聯關係,但Product集合數據並無刪除,依然存在上下文的內存中,只是它再也不與Category關聯。

後面咱們又從新使用Load加載Product但category.Products.Count()的值依然爲0。這正好說明Clear方法的本質。

這是由於Load默認使用MergeOption.AppendOnly的方式加載數據,找不到Product實例了(被Clear了)。而後咱們使用MergeOption.OverwriteChanges的方式纔將數據從新關聯

 

  關於MergeOption(實體合併選項) 

MergeOption枚舉共有4個選項

1.MergeOption.AppendOnly

  在現有的關係基礎上合併
2.MergeOption.OverwriteChanges

  OverwriteChanges模式,會從數據庫中更新當前上下文實例的值(使用同一個實體對象的實例,如category)。

  當你想要恢復實體在上下文的改變,從數據庫刷新實體時,使用這個模式就很是有用。

3.MergeOption.NoTracking

  無追蹤模式不會跟蹤對象的變化,也不會意識到對象已經被加載到當前上下文

  NoTracking能夠應用到一個實體的導航屬性(關聯實體屬性),但這個實體也必須使用NoTracking

  反過來,NoTracking應用到某個實體時,這個實體的導航屬性會忽略默認的AppendOnly模式而使用NoTracking模式
4.MergeOption.PreserveChanges

  PreserveChanges選項本質上與OverwriteChanges選項相反,

  PreserveChanges模式會更新查詢結果集(若數據庫中有變化),但不會更新在當前上下文內存中的值

PS: 也許你已經注意到,這裏咱們使用了ObjectContext對象,而不是context對象,這是由於DbContext不能直接使用MergeOption類型,因此必須使用ObjectContext對象

  關於性能提高                                

在任什麼時候候,關聯實體集合的數量被限制時,使用Load方法,會有助於咱們提高程序的性能,好比加載一個Category中的5條Product。

在少許場景中,咱們須要整個關聯集合時,也可使用Load

PS:當一個實體在Added(添加)、Deleted(刪除)、Detected(分離)狀態時,Load方法沒法使用。

關於Entity Framework性能提高的方法還有不少,固然,咱們必須根據本身的項目實際狀況來作優化。不一樣的應用場景,選擇不一樣的優化方案

 

示例代碼:點此下載

轉載自:https://www.cnblogs.com/jayshsoft/p/entity-framework-explicit-loading.html

相關文章
相關標籤/搜索