翻譯的初衷以及爲何選擇《Entity Framework 6 Recipes》來學習,請看本系列開篇html
前一章,咱們展現了常見數據庫場景的建模方式,本章將向你展現如何查詢實體數據模型,通常來講,有三種方式:數據庫
一、LINQ to Entities;編程
二、Entity SQL;api
三、Native SQL;框架
咱們將在本章演示這三種方式,爲了幫助你理解實體框架查詢的基本知識,本章覆蓋了常見和不常見的場景。同時咱們也展現了實體框架6新的查詢功能。異步
你有一個長耗時的實體框架查詢,當執行查詢時,你不想打斷應用程序主線程運行,在數據返加以前,能讓用戶作一些別的操做。同時,使用LINQ to Entities來查詢模型也同樣重要,它是查詢數據庫模型的首選方案。async
解決方案ide
假設你有如圖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/