《Entity Framework 6 Recipes》中文翻譯系列 (18) -----第三章 查詢之結果集扁平化和多屬性分組

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

3-14  結果集扁平化

問題sql

  你有一對多關聯的兩個實體,你想經過一個查詢,獲取關聯中的兩個實體的扁平化投影。扁平化或者叫壓縮,這是不規範的叫法。它是指一個有父類和子類的對象圖,被投影到一個單獨的類中。數據庫

解決方案app

  假設你有一對擁有一對多關聯的實體,如圖3-15所示的模型。cors

圖3-15 模型中,一個表明助理的Associate的實體類型和一個表明助理工資歷史的AssociateSalary實體框架

 

  你想在一個查詢中獲取全部的associates 和他們的工資歷史,可能有些新員工在系統尚未工資記錄。 你但願查詢結果集中能包含這些關聯。ide

  請按代碼清單3-30的方式,查詢模型並獲取你想要的結果集。函數

代碼清單3-30. 使用LINQ和Entity SQL扁平化結果集學習

 1 using (var context = new EFRecipesEntities())
 2             {
 3                 // 刪除以前的測試數據
 4                 context.Database.ExecuteSqlCommand("delete from chapter3.associatesalary");
 5                 context.Database.ExecuteSqlCommand("delete from chapter3.associate");
 6                 // 添加新的測試數據
 7                 var assoc1 = new Associate { Name = "Janis Roberts" };
 8                 var assoc2 = new Associate { Name = "Kevin Hodges" };
 9                 var assoc3 = new Associate { Name = "Bill Jordan" };
10                 var salary1 = new AssociateSalary
11                 {
12                     Salary = 39500M,
13                     SalaryDate = DateTime.Parse("8/4/09")
14                 };
15                 var salary2 = new AssociateSalary
16                 {
17                     Salary = 41900M,
18                     SalaryDate = DateTime.Parse("2/5/10")
19                 };
20                 var salary3 = new AssociateSalary
21                 {
22                     Salary = 33500M,
23                     SalaryDate = DateTime.Parse("10/08/09")
24                 };
25                 assoc2.AssociateSalaries.Add(salary1);
26                 assoc2.AssociateSalaries.Add(salary2);
27                 assoc3.AssociateSalaries.Add(salary3);
28                 context.Associates.Add(assoc1);
29                 context.Associates.Add(assoc2);
30                 context.Associates.Add(assoc3);
31                 context.SaveChanges();
32             }
33 
34             using (var context = new EFRecipesEntities())
35             {
36                 Console.WriteLine("Using LINQ...");
37                 var allHistory = from a in context.Associates
38                                  from ah in a.AssociateSalaries.DefaultIfEmpty()
39                                  orderby a.Name
40                                  select new
41                                  {
42                                      Name = a.Name,
43                                      Salary = (decimal?)ah.Salary,
44                                      Date = (DateTime?)ah.SalaryDate
45                                  };
46 
47                 Console.WriteLine("Associate Salary History");
48                 foreach (var history in allHistory)
49                 {
50                     if (history.Salary.HasValue)
51                         Console.WriteLine("{0} Salary on {1} was {2}", history.Name,
52                                            history.Date.Value.ToShortDateString(),
53                                            history.Salary.Value.ToString("C"));
54                     else
55                         Console.WriteLine("{0} --", history.Name);
56                 }
57             }
58 
59             using (var context = new EFRecipesEntities())
60             {
61                 Console.WriteLine("\nUsing Entity SQL...");
62                 var esql = @"select a.Name, h.Salary, h.SalaryDate
63                  from Associates as a outer apply 
64                    a.AssociateSalaries as h order by a.Name";
65                 var allHistory = ((IObjectContextAdapter)context).ObjectContext.CreateQuery<DbDataRecord>(esql);
66                 Console.WriteLine("Associate Salary History");
67                 foreach (var history in allHistory)
68                 {
69                     if (history["Salary"] != DBNull.Value)
70                         Console.WriteLine("{0} Salary on {1:d} was {2:c}", history["Name"],
71                                            history["SalaryDate"], history["Salary"]);
72                     else
73                         Console.WriteLine("{0} --", history["Name"]);
74                 }
75             }
76 
77             Console.WriteLine("\nPress <enter> to continue...");
78             Console.ReadLine();
79         }

  這裏的訣竅是,咱們「扁平化」(flatten)層次結構的數據,好比,一個associate和多個 salary輸入。代碼清單3-30輸出以下:測試

1 Using LINQ...
2 Associate Salary History
3 Bill Jordan Salary on 10/8/2009 was $33,500.00
4 Janis Roberts --Kevin Hodges Salary on 8/4/2009 was $39,500.00
5 Kevin Hodges Salary on 2/5/2010 was $41,900.00
6 Using Entity SQL...
7 Bill Jordan Salary on 10/8/2009 was $33,500.00
8 Janis Roberts --Kevin Hodges Salary on 8/4/2009 was $39,500.00
9 Kevin Hodges Salary on 2/5/2010 was $41,900.00

原理

  爲了扁平化結果集,咱們使用了3-10中的策略、使用嵌套形式的from從句和DefaultIfEmpty()方法來得到兩張表的一個左外鏈接。方法DefaultIfEmpty()能確保咱們有左邊表(Associate 實體)的全部行,即便右邊(AssociateSalary實體)沒有與它對應的行。咱們將結果集投影到一個匿名類型,當沒有AssociateSalary實體與Associate實體對應時,當心屬性salary 和 salarydate獲得null值。

  對於Entity SQL 解決方案,咱們使用  outer apply 操做符建立Associate實體和AssociateSalary實體之間的匹配。 在SQL Server中,可使用corss和outer apply操做符。

 

3-15  使用多屬性分組

問題

  你想在一個查詢中使用多屬性對結果集進行分組,以至在數據庫中執行查詢時使用多列進行分組。

解決方案

  假設你有包含一個Enent實體的模型,如圖3-16所示。Event有屬性name,city和sate。你想經過state和city對events進行分組。

圖3-16 包含一個Enent實體的模型,Event有屬性name,city和sate屬性

 

  在代碼清單3-31中,咱們使用Code-First方法建立了實體類。

代碼清單3-31.實體類型

1 public class Event
2     {
3         public int EventId { get; set; }
4         public string Name { get; set; }
5         public string State { get; set; }
6         public string City { get; set; }
7     }

  接下來,代碼清單3-32中建立了上下文對象,它是用Code-First方法訪問實體框架功能的入口。

代碼清單3-32.  上下文對象

 1  public class EFRecipesEntities : DbContext
 2     {
 3         public EFRecipesEntities()
 4             : base("ConnectionString") {}
 5 
 6         public DbSet<Event> Events { get; set; }
 7 
 8         protected override void OnModelCreating(DbModelBuilder modelBuilder)
 9         {
10             modelBuilder.Entity<Event>().ToTable("Chapter3.Event");
11             base.OnModelCreating(modelBuilder);
12         }
13     }

  使用代碼清單3-33中的代碼,獲取全部的events並按sate和city對結果集分組

代碼清單3-33 多屬性分組

 1  using (var context = new EFRecipesEntities())
 2             {
 3                 // 刪除以前的測試數據
 4                 context.Database.ExecuteSqlCommand("delete from chapter3.event");
 5                 //添加新的測試數據
 6                 context.Events.Add(new Event
 7                 {
 8                     Name = "TechFest 2010",
 9                     State = "TX",
10                     City = "Dallas"
11                 });
12                 context.Events.Add(new Event
13                 {
14                     Name = "Little Blue River Festival",
15                     State = "MO",
16                     City = "Raytown"
17                 });
18                 context.Events.Add(new Event
19                 {
20                     Name = "Fourth of July Fireworks",
21                     State = "MO",
22                     City = "Raytown"
23                 });
24                 context.Events.Add(new Event
25                 {
26                     Name = "BBQ Ribs Championship",
27                     State = "TX",
28                     City = "Dallas"
29                 });
30                 context.Events.Add(new Event
31                 {
32                     Name = "Thunder on the Ohio",
33                     State = "KY",
34                     City = "Louisville"
35                 });
36                 context.SaveChanges();
37             }
38 
39             using (var context = new EFRecipesEntities())
40             {
41                 Console.WriteLine("Using LINQ");
42                 var results = from e in context.Events
43                               // 使用匿名類型封閉複合key State 和City
44                               group e by new { e.State, e.City } into g
45                               select new
46                               {
47                                   State = g.Key.State,
48                                   City = g.Key.City,
49                                   Events = g
50                               };
51                 Console.WriteLine("Events by State and City...");
52                 foreach (var item in results)
53                 {
54                     Console.WriteLine("{0}, {1}", item.City, item.State);
55                     foreach (var ev in item.Events)
56                     {
57                         Console.WriteLine("\t{0}", ev.Name);
58                     }
59                 }
60             }
61 
62             using (var context = new EFRecipesEntities())
63             {
64                 Console.WriteLine("\nUsing Entity SQL");
65                 var esql = @"select e.State, e.City, GroupPartition(e) as Events
66                  from Events as e
67                  group by e.State, e.City";
68                 var records = ((IObjectContextAdapter)context).ObjectContext.CreateQuery<DbDataRecord>(esql);
69                 Console.WriteLine("Events by State and City...");
70                 foreach (var rec in records)
71                 {
72                     Console.WriteLine("{0}, {1}", rec["City"], rec["State"]);
73                     var events = (List<Event>)rec["Events"];
74                     foreach (var ev in events)
75                     {
76                         Console.WriteLine("\t{0}", ev.Name);
77                     }
78                 }
79             }
80 
81             Console.WriteLine("\nPress <enter> to continue...");
82             Console.ReadLine();

代碼清單3-33的輸出以下:

Using LINQ
Events by State and City...
Louisville, KY
Thunder on the Ohio
Raytown, MO
Little Blue River Festival
Fourth of July Fireworks
Dallas, TX
TechFest 2010
BBQ Ribs Championship
Using Entity SQL
Events by State and City...
Louisville, KY
Thunder on the Ohio
Raytown, MO
Little Blue River Festival
Fourth of July Fireworks
Dallas, TX
TechFest 2010
BBQ Ribs Championship

 

原理

   在代碼清單3-33中,針對這個問題,展現了兩種不一樣的方法。 第一種方法使用LINQ和group by 操做符按sate和city對結果集進行分組。當用group by進行多屬性分組時, 咱們建立了一個匿名類型對數據進行分組。 使用into從句將分組放到g中,它是咱們存放查詢結果集的第二個序列。

  咱們把結果集從g中投影到第二個匿名類型中,經過從分組key的字段State(第一個匿名類型中)獲取State值,從分組key的字段City中獲取 City值,對於events 我只是簡單地把分組的所有成員分配給它。

  對於Entity SQL方法,咱們只能投影group by 從句使用的列、常量或者從聚合函數計算獲得的值。在咱們示例中,咱們投影state、city和分組中的events.

 

 

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

謝謝你們的持續關注,個人博客地址:http://www.cnblogs.com/VolcanoCloud/

相關文章
相關標籤/搜索