《Entity Framework 6 Recipes》中文翻譯系列 (27) ------ 第五章 加載實體和導航屬性之關聯實體過濾、排序、執行聚合操做

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

5-9  關聯實體過濾和排序

問題html

  你有一實體的實例,你想加載應用了過濾和排序的相關實體。java

解決方案數據庫

  假設你有如圖5-24所示的概念模型框架

圖5-24 一個酒店預約系統的模型ide

 

   假設咱們有一個酒店(Hotel)實體,使用代碼清單5-22,獲取酒店的商務套房(executive suite),查看是否被預約,並按房價排序。函數

代碼清單5-22.經過方法Entry()和Query()顯式加載實體集合,並對集合過慮和排序學習

 1 using (var context = new EFRecipesEntities())
 2             {
 3                 var hotel = new Hotel {Name = "Grand Seasons Hotel"};
 4                 var r101 = new Room {Rate = 79.95M, Hotel = hotel};
 5                 var es201 = new ExecutiveSuite {Rate = 179.95M, Hotel = hotel};
 6                 var es301 = new ExecutiveSuite {Rate = 299.95M, Hotel = hotel};
 7 
 8                 var res1 = new Reservation
 9                     {
10                         StartDate = DateTime.Parse("3/12/2010"),
11                         EndDate = DateTime.Parse("3/14/2010"),
12                         ContactName = "Roberta Jones",
13                         Room = es301
14                     };
15                 var res2 = new Reservation
16                     {
17                         StartDate = DateTime.Parse("1/18/2010"),
18                         EndDate = DateTime.Parse("1/28/2010"),
19                         ContactName = "Bill Meyers",
20                         Room = es301
21                     };
22                 var res3 = new Reservation
23                     {
24                         StartDate = DateTime.Parse("2/5/2010"),
25                         EndDate = DateTime.Parse("2/6/2010"),
26                         ContactName = "Robin Rosen",
27                         Room = r101
28                     };
29 
30                 es301.Reservations.Add(res1);
31                 es301.Reservations.Add(res2);
32                 r101.Reservations.Add(res3);
33 
34                 hotel.Rooms.Add(r101);
35                 hotel.Rooms.Add(es201);
36                 hotel.Rooms.Add(es301);
37 
38                 context.Hotels.Add(hotel);
39                 context.SaveChanges();
40             }
41 
42             using (var context = new EFRecipesEntities())
43             {
44                 // 假設咱們擁有一個Hotel實例
45                 var hotel = context.Hotels.First();
46 
47                 //使用Load()方法顯式加載,給經過Include()獲取的關聯數據提供過濾的機會
48                 context.Entry(hotel)
49                        .Collection(x => x.Rooms)
50                        .Query()
51                        .Include(y => y.Reservations)
52                        .Where(y => y is ExecutiveSuite && y.Reservations.Any())
53                        .Load();
54 
55                 Console.WriteLine("Executive Suites for {0} with reservations", hotel.Name);
56 
57                 foreach (var room in hotel.Rooms)
58                 {
59                     Console.WriteLine("\nExecutive Suite {0} is {1} per night", room.RoomId,
60                                       room.Rate.ToString("C"));
61                     Console.WriteLine("Current reservations are:");
62                     foreach (var res in room.Reservations.OrderBy(r => r.StartDate))
63                     {
64                         Console.WriteLine("\t{0} thru {1} ({2})", res.StartDate.ToShortDateString(),
65                                           res.EndDate.ToShortDateString(), res.ContactName);
66                     }
67                 }
68             }
69 
70             Console.WriteLine("Press <enter> to continue...");
71             Console.ReadLine();

代碼清單5-22的輸出以下:ui

Executive Suites for Grand Seasons Hotel with reservations
Executive Suite 65 is $299.95 per night
Current reservations are:
1/18/2010 thru 1/28/2010 (Bill Meyers)
3/12/2010 thru 3/14/2010 (Roberta Jones)
Executive Suite 64 is $79.95 per night
Current reservations are:
2/5/2010 thru 2/6/2010 (Robin Rosen)
Executive Suite 66 is $179.95 per night

原理this

  代碼清單5-22,使用顯式加載來獲取關聯實體集合,並在對集合進行過濾和排序。

  和延遲加載、預先加載一塊兒,顯式加載(Explicit loading)是加載關聯實體的第三種選擇。使用顯示加載時,你能夠對它進行徹底的控制。若是你用 它,你能夠控制是否,什麼時候,何地將關聯實體加載到上下文中。

  爲了使用顯示加載,咱們使用了Dbcontext上下文對象公佈的Entry()方法,它接受一個你但願查詢實體的父類做爲參數。Entry()方法提供了大量的關於實體的信息,包含經過使用方法collection()和Reference()訪問關聯實體。

  在上面的示例中,咱們使用父實體Hotel做爲Entry()的參數,而後鏈式調用Collection()方法,並傳遞導航屬性Rooms,做爲它的參數。DbCollectionEntry類的Query()方法,產生一個查詢,到數據庫中去加載room實體。

  最後,咱們使用導航屬性Reservations做爲Include()方法的參數,爲每一個room預先加載關聯的實體reservations。應用Where從句過濾獲取類型爲ExecuteiveSuite的,至少有一個預約的Room集合。而後使用OrderBy從句按房價對集合排序。

  通常地,使用Include()方法爲一個父實體返回全部的關聯實體,但沒有機會過濾和操做結果集。這個規則的一個例外就是,應用了顯示加載。如示例演示的那樣,咱們能對關聯的實體Reservations的結果集進行過濾和排序。

  記住,咱們只能使用這種方式對Include()爲一個父實體返回的關聯實體集進行過濾。這個特性在延遲加載和預先加載中無效。

 

 

5-10  在關聯實體上執行聚合操做

問題

  你想在一個關聯實體集合上應用一個聚合操做,可是不加載整個集合。另外,你想使用Code-First管理數據訪問。

 

解決方案

  假設你有如圖5-25所示的概念模型

圖5-25 包含一個訂單和它的訂單項的模型

  

  在Visual Studio中添加一個名爲Recipe10的控制檯應用,並確保引用了實體框架6的庫,NuGet能夠很好的完成這個任務。在Reference目錄上右鍵,並選擇 Manage NeGet Packages(管理NeGet包),在Online頁,定位並安裝實體框架6的包。這樣操做後,NeGet將下載,安裝和配置實體框架6的庫到你的項目中。

  接下來咱們建立兩個實體對象:Order和OrderItem,複製代碼清單5-23中的屬性到這兩個類中。

代碼清單5-23. 實體類

 public class Order 
    {
        public Order()
        {
            OrderItems = new HashSet<OrderItem>();
        }

        public int OrderId { get; set; }
        public System.DateTime OrderDate { get; set; }
        public string CustomerName { get; set; }

        public virtual ICollection<OrderItem> OrderItems { get; set; }
    }


 public class OrderItem
    {
        public int OrderItemId { get; set; }
        public int OrderId { get; set; }
        public int SKU { get; set; }
        public int Shipped { get; set; }
        public decimal UnitPrice { get; set; }

        public virtual Order Order { get; set; }
    }

  接下來,建立一個名爲Recipe10Context的類,並將代碼清單5-24中的代碼添加到其中,並確保其派生到DbContext類。

代碼清單5-24. 上下文

 1  public class Recipe10Context : DbContext
 2     {
 3         public Recipe10Context()
 4             : base("Recipe10ConnectionString")
 5         {
 6             //禁用實體框架的模型兼容
 7             Database.SetInitializer<Recipe10Context>(null);
 8         }
 9 
10         protected override void OnModelCreating(DbModelBuilder modelBuilder)
11         {
12             modelBuilder.Entity<Order>().ToTable("Chapter5.Order");
13             modelBuilder.Entity<OrderItem>().ToTable("Chapter5.OrderItem");
14         }
15 
16         public DbSet<Order> Orders { get; set; }
17         public DbSet<OrderItem> OrderItems { get; set; }
18     }

接下來添加App.Config文件到項目中,並使用代碼清單5-25中的代碼添加到文件的ConnectionStrings小節下。

代碼清單5-25. 鏈接字符串

複製代碼
 
<connectionStrings>
<add name="Recipe10ConnectionString" connectionString="Data Source=.; Initial Catalog=EFRecipes; Integrated Security=True; MultipleActiveResultSets=True" providerName="System.Data.SqlClient" /> </connectionStrings>

  在圖5-25中,咱們有一個簡單的模型,它由一個訂單(Order)和訂單上的產品(OrderItems集合)組成。爲訂單計算總價的一種方法是,使用Load()方法加載訂單項的整個集合,而後枚舉它並對訂單項進行求合計算。

  另外一種方法是,將計算的過程放到數據庫中去,讓它完成計算後返回。第二方法的優勢是,它避免了爲實現這個惟一的目標而實例化每一個訂單項的潛在成本。代碼清單5-26演示了這種方法。

代碼清單5-26.在不加載關聯實體的狀況下,對其運用聚合函數

 1  using (var context = new Recipe10Context())
 2             {
 3                 var order = new Order {CustomerName = "Jenny Craig", OrderDate = DateTime.Parse("3/12/2010")};
 4 
 5                 var item1 = new OrderItem {Order = order, Shipped = 3, SKU = 2827, UnitPrice = 12.95M};
 6                 var item2 = new OrderItem {Order = order, Shipped = 1, SKU = 1918, UnitPrice = 19.95M};
 7                 var item3 = new OrderItem {Order = order, Shipped = 3, SKU = 392, UnitPrice = 8.95M};
 8 
 9                 order.OrderItems.Add(item1);
10                 order.OrderItems.Add(item2);
11                 order.OrderItems.Add(item3);
12 
13                 context.Orders.Add(order);
14                 context.SaveChanges();
15             }
16 
17             using (var context = new Recipe10Context())
18             {
19                 // 假設咱們有一個Order實體
20                 var order = context.Orders.First();
21 
22                 // 獲取訂單總價
23                 var amt = context.Entry(order)
24                     .Collection(x => x.OrderItems)
25                     .Query()
26                     .Sum(y => y.Shipped*y.UnitPrice);
27 
28                 Console.WriteLine("Order Number: {0}", order.OrderId);
29                 Console.WriteLine("Order Date: {0}", order.OrderDate.ToShortDateString());
30                 Console.WriteLine("Order Total: {0}", amt.ToString("C"));
31             }
32 
33             Console.WriteLine("Press <enter> to continue...");
34             Console.ReadLine();

代碼清單5-26的輸出以下:

Order Number: 6
Order Date: 3/12/2010
Order Total: $85.65

原理

  在代碼清單5-26中,實現了顯示加載,一開始,咱們使用了DbContext上下文對象中公佈的Entry()方法。Entry()方法接受一個Oder對象做爲參數,它是咱們但願查詢對象的父實體。Entry()方法提供了關於Order的大量信息,包含經過方法Collection()和Reference()訪問其關聯實體對象。

  在上面的示例中,咱們經過鏈式調用Collection()方法並傳遞導航屬性,OrderItems,做爲參數來查詢關聯的訂單項。DbCollectionEntry類中的方法Query()產生一個從數據庫中加載訂單項的查詢。

  最後,咱們應用LINQ擴展方法Sum(),並傳遞一個lambda表達式來計算訂單總價。整個表達式被轉換成相應的存儲層命令並執行,這樣就爲咱們節省了實例化每個訂單項的成本。

  這個簡單示例演示了,經過靈活組合方法Entry()和Query()來實現顯示加載,這兩個方法會修改獲取關聯實體集合(OrderItems)的查詢。這樣,咱們就能憑藉這個查詢,在不加載訂單項的狀況下爲訂單計算訂單項的合計金額。

 

 

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

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

相關文章
相關標籤/搜索