翻譯的初衷以及爲何選擇《Entity Framework 6 Recipes》來學習,請看本系列開篇javascript
問題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()爲一個父實體返回的關聯實體集進行過濾。這個特性在延遲加載和預先加載中無效。
問題
你想在一個關聯實體集合上應用一個聚合操做,可是不加載整個集合。另外,你想使用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/