《Entity Framework 6 Recipes》中文翻譯系列 (11) -----第三章 查詢之異步查詢


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

第三章 查詢

  前一章,我們展示了常見數據庫場景的建模方式,本章將向你展示如何查詢實體數據模型,一般來說,有三種方式:

    1、LINQ to Entities;

    2、Entity SQL;

    3、Native SQL;

  我們將在本章演示這三種方式,為了幫助你理解實體框架查詢的基本知識,本章覆蓋了常見和不常見的場景。同時我們也展示了實體框架6新的查詢功能。

 

3-1.異步查詢

  你有一個長耗時的實體框架查詢,當執行查詢時,你不想打斷應用程序主線程運行,在數據返加之前,能讓用戶做一些別的操作。同時,使用LINQ to Entities來查詢模型也一樣重要,它是查詢數據庫模型的首選方案。

解決方案

  假設你有如圖3-1所示的模型。

圖3-1 模型中,一個代表助理的Associate的實體類型和一個代表助理工資歷史的AssociateSalary實體

  

  在這個模型中,我們有兩個代表助理和他們工資歷史的實體。

  作為開始,我們在示例中使用Code-First方法創建類,在代碼清單3-1中,創建這些實體類。

代碼清單3-1  Associate 和 AssociateSalary實體類型

 1 public class Associate
 2     {
 3         public Associate()
 4         {
 5             AssociateSalaries = new HashSet<AssociateSalary>();
 6         }
 7         public int AssociateId { get; set; }
 8         public string Name { get; set; }
 9         public virtual ICollection<AssociateSalary> AssociateSalaries { get; set; }
10     }
11     public class AssociateSalary
12     {
13         public int SalaryId { get; set; }
14         public int AssociateId { get; set; }
15         public decimal Salary { get; set; }
16         public DateTime SalaryDate { get; set; }
17         public virtual Associate Associate { get; set; }
18     }

 

   接下來,代碼清單3-2使用Code-First創建DbContext上下文對象,注意在OnModelCreateing方法中,我們顯示地將SalaryId屬性映射為AssociateSalary表的主鍵。當我們使用Code Firtst時,如果一個屬性的名字是Id或者<表名>Id,實體框架為假定該屬性是對應表的主鍵。另外,像這里這樣,要顯式設置主鍵。

 代碼清單3-2 Dbcontext上下文對象

 1 public class EFRecipesEntities : DbContext
 2     {
 3         public EFRecipesEntities()
 4             : base("ConnectionString")
 5         {
 6         }
 7 
 8         public DbSet<Associate> Associates { get; set; }
 9         public DbSet<AssociateSalary> AssociateSalaries { get; set; }
10 
11         protected override void OnModelCreating(DbModelBuilder modelBuilder)
12         {
13             modelBuilder.Entity<Associate>().ToTable("Chapter3.Associate");
14             modelBuilder.Entity<AssociateSalary>().ToTable("Chapter3.AssociateSalary");
15             
16             //顯示分配實體鍵為AssociateSalary表的主鍵,以免實體框架使用默認映射約定
17             modelBuilder.Entity<AssociateSalary>().HasKey(x => x.SalaryId);
18             base.OnModelCreating(modelBuilder);
19         }
20     }

 

   代碼清單3-3 演示,如何借助新實體框架的異步方法實現異步查詢,刪除、加載、獲取數據。

 

代碼清單3-3.異步處理實體框架查詢

 

  1  private static void Main()
  2         {
  3             var asyncTask = EF6AsyncDemo();
  4 
  5             foreach (var c in BusyChars())
  6             {
  7                 if (asyncTask.IsCompleted)
  8                 { 
  9                     break;
 10                 }
 11                 Console.Write(c);
 12                 Console.CursorLeft = 0;
 13                 Thread.Sleep(100);
 14             }
 15             Console.WriteLine("\nPress <enter> to continue...");
 16             Console.ReadLine();
 17         }
 18 
 19         private static IEnumerable<char> BusyChars()
 20         {
 21             while (true)
 22             {
 23                 yield return '\\';
 24                 yield return '|';
 25                 yield return '/';
 26                 yield return '-';
 27             }
 28         }
 29 
 30         private static async Task EF6AsyncDemo()
 31         {
 32             await Cleanup();
 33             await LoadData();
 34             await RunForEachAsyncExample();
 35             await RunToListAsyncExampe();
 36             await RunSingleOrDefaultAsyncExampe();
 37         }
 38 
 39         private static async Task Cleanup()
 40         {
 41             using (var context = new EFRecipesEntities())
 42             {
 43                 // 清除原始數據
 44                 // 異步執行原始SQL語句             
 45                 Console.WriteLine("Cleaning Up Previous Test Data");
 46                 Console.WriteLine("=========\n");
 47                 
 48                 await context.Database.ExecuteSqlCommandAsync("delete from chapter3.AssociateSalary");
 49                 await context.Database.ExecuteSqlCommandAsync("delete from chapter3.Associate");
 50                 await Task.Delay(5000);
 51             }
 52         }
 53 
 54         private static async Task LoadData()
 55         {
 56             using (var context = new EFRecipesEntities())
 57             {
 58                 // 添加測試數據
 59                 Console.WriteLine("Adding Test Data");
 60                 Console.WriteLine("=========\n");
 61 
 62                 var assoc1 = new Associate { Name = "Janis Roberts" };
 63                 var assoc2 = new Associate { Name = "Kevin Hodges" };
 64                 var assoc3 = new Associate { Name = "Bill Jordan" };
 65                 var salary1 = new AssociateSalary
 66                 {
 67                     Salary = 39500M,
 68                     SalaryDate = DateTime.Parse("8/4/09")
 69                 };
 70                 var salary2 = new AssociateSalary
 71                 {
 72                     Salary = 41900M,
 73                     SalaryDate = DateTime.Parse("2/5/10")
 74                 };
 75                 var salary3 = new AssociateSalary
 76                 {
 77                     Salary = 33500M,
 78                     SalaryDate = DateTime.Parse("10/08/09")
 79                 };
 80                 assoc1.AssociateSalaries.Add(salary1);
 81                 assoc2.AssociateSalaries.Add(salary2);
 82                 assoc3.AssociateSalaries.Add(salary3);
 83                 context.Associates.Add(assoc1);
 84                 context.Associates.Add(assoc2);
 85                 context.Associates.Add(assoc3);
 86 
 87                 // 異步保存
 88                 await context.SaveChangesAsync();
 89                 await Task.Delay(5000);
 90             }
 91         }
 92 
 93         private static async Task RunForEachAsyncExample()
 94         {
 95             using (var context = new EFRecipesEntities())
 96             {
 97                 Console.WriteLine("Async ForEach Call");
 98                 Console.WriteLine("=========");
 99 
100                 // 借助 ForEachAsync 方法
101                 await context.Associates.Include(x => x.AssociateSalaries).ForEachAsync(x =>
102                 {
103                     Console.WriteLine("Here are the salaries for Associate {0}:", x.Name);
104 
105                     foreach (var salary in x.AssociateSalaries)
106                     {
107                         Console.WriteLine("\t{0}", salary.Salary);
108                     }
109                 });
110                 await Task.Delay(5000);
111             }
112         }
113 
114         private static async Task RunToListAsyncExampe()
115         {
116             using (var context = new EFRecipesEntities())
117             {
118                 Console.WriteLine("\n\nAsync ToList Call");
119                 Console.WriteLine("=========");
120 
121                 // 借助 ToListAsync 方法
122                 var associates = await context.Associates.Include(x => x.AssociateSalaries).OrderBy(x => x.Name).ToListAsync();
123 
124                 foreach (var associate in associates)
125                 {
126                     Console.WriteLine("Here are the salaries for Associate {0}:", associate.Name);
127                     foreach (var salaryInfo in associate.AssociateSalaries)
128                     {
129                         Console.WriteLine("\t{0}", salaryInfo.Salary);
130                     }  
131                 }
132                 await Task.Delay(5000);
133             }
134         }
135 
136         private static async Task RunSingleOrDefaultAsyncExampe()
137         {
138             using (var context = new EFRecipesEntities())
139             {
140                 Console.WriteLine("\n\nAsync SingleOrDefault Call");
141                 Console.WriteLine("=========");
142 
143                 var associate = await context.Associates.
144                     Include(x => x.AssociateSalaries).
145                     OrderBy(x => x.Name).
146                     FirstOrDefaultAsync(y => y.Name == "Kevin Hodges");
147 
148                 Console.WriteLine("Here are the salaries for Associate {0}:", associate.Name);
149                 foreach (var salaryInfo in associate.AssociateSalaries)
150                 {
151                     Console.WriteLine("\t{0}", salaryInfo.Salary);
152                 }
153                 await Task.Delay(5000);
154             }
155         }

 

代碼清單3-3輸出如下:

Cleaning Up Previous Test Data
=========
Adding Test Data
=========
Async ForEach Call
=========
Here are the salaries for Associate Janis Roberts:
39500.00
Here are the salaries for Associate Kevin Hodges:
41900.00
Here are the salaries for Associate Bill Jordan:
33500.00
Async ToList Call
=========
Here are the salaries for Associate Bill Jordan:
33500.00
Here are the salaries for Associate Janis Roberts:
39500.00
Here are the salaries for Associate Kevin Hodges:
41900.00
Async SingleOrDefault Call
=========
Here are the salaries for Associate Kevin Hodges:
41900.00

 

原理

  在這個示例中,我們演示了實體框架的兩個關鍵概念的用途:使用LINQ擴展查詢模型以及實體框架6中實現的新的異步功能。

  對於絕大多數的查詢操作,你都需要用到LINQ。這樣做會給你帶來,智能提示、編譯時檢查,以及強類型的編程體驗。如果你需要在運行時動態構建查詢,你可以考慮使用Entity SQL,它能連接查詢表達式各個部分的字符串。你將在本節后面看到相關的示例。

  開始時,我們先清除之前數據庫中的測試數據。請注意我們是如何把Cleanup()操作包裝在一個異步方法中的。然后我們生成原始的SQL語句,並調用新的ExecuteSqlCommandAsync()方法。請注意我們是如何憑借.NET framework4.5中的async/await異步模式。這種模式能夠不通過顯示實例化一個后台線程來實現異步;此外,它釋放當前等待數據庫操作完成的CLR線程控制權。(譯注:也就是不卡住當前線程 ,讓它可以繼續執行別的操作)。

  接下來,我們加載測試數據Associate和AssoicateSalareies。為了執行異步調用,像前面一樣,我們將LoadData()操作包裝在一個異步方法中,並通過最新增加的SaveChangesAsync()方法在數據庫中插入測試數據。

  接下為,我們呈現了三種不同的模型查詢方式,每種方式都憑借了實體框架中的LINQ擴展,每種方式都憑借await/async模式包含在一個異步方法中。在RunForEachAsyncExample()方法中,由於沒有與foreach語句相匹配異步方法,我們使用了ForEachAsync()擴展方法。憑借這個異步方法以及 Inclued()方法,我們能夠異步查詢和枚舉這些對象。

  在隨后的RunToListAsyncExample()和RunSingeOrDefaultAsyncExample()查詢中,我們憑借ToList()和SingleOrDefault()方法的新的異步方法來實現。

  實體框架現在公布了大量的異步操作方法。它們的命名約定是,在已存在的api名稱中加上后綴Asyn,使其相對簡單地在添加或者獲取數據時實現異步。

 

 

實體框架交流QQ群:  458326058,歡迎有興趣的朋友加入一起交流

謝謝大家的持續關注,我的博客地址:http://www.cnblogs.com/VolcanoCloud/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM