翻译的初衷以及为什么选择《Entity Framework 6 Recipes》来学习,请看本系列开篇

5-9  关联实体过滤和排序

问题

  你有一实体的实例,你想加载应用了过滤和排序的相关实体。

解决方案

  假设你有如图5-24所示的概念模型

图5-24 一个酒店预定系统的模型

  假设我们有一个酒店(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的输出如下:

  1. Executive Suites for Grand Seasons Hotel with reservations
  2. Executive Suite is $299.95 per night
  3. Current reservations are:
  4. // thru // (Bill Meyers)
  5. // thru // (Roberta Jones)
  6. Executive Suite is $79.95 per night
  7. Current reservations are:
  8. // thru // (Robin Rosen)
  9. Executive Suite is $179.95 per night

原理

  代码清单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. 实体类

  1. public class Order
  2. {
  3. public Order()
  4. {
  5. OrderItems = new HashSet<OrderItem>();
  6. }
  7.  
  8. public int OrderId { get; set; }
  9. public System.DateTime OrderDate { get; set; }
  10. public string CustomerName { get; set; }
  11.  
  12. public virtual ICollection<OrderItem> OrderItems { get; set; }
  13. }
  14.  
  15. public class OrderItem
  16. {
  17. public int OrderItemId { get; set; }
  18. public int OrderId { get; set; }
  19. public int SKU { get; set; }
  20. public int Shipped { get; set; }
  21. public decimal UnitPrice { get; set; }
  22.  
  23. public virtual Order Order { get; set; }
  24. }

  接下来,创建一个名为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. 连接字符串

 
  1. <connectionStrings>
  2. <add name="Recipe10ConnectionString"
  3. connectionString="Data Source=.;
  4. Initial Catalog=EFRecipes;
  5. Integrated Security=True;
  6. MultipleActiveResultSets=True"
  7. providerName="System.Data.SqlClient" />
  8. </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 = , SKU = , UnitPrice = 12.95M};
  6. var item2 = new OrderItem {Order = order, Shipped = , SKU = , UnitPrice = 19.95M};
  7. var item3 = new OrderItem {Order = order, Shipped = , SKU = , 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的输出如下:

  1. Order Number:
  2. Order Date: //
  3. 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/

《Entity Framework 6 Recipes》中文翻译系列 (27) ------ 第五章 加载实体和导航属性之关联实体过滤、排序、执行聚合操作的更多相关文章

  1. 《Entity Framework 6 Recipes》中文翻译系列 (22) -----第五章 加载实体和导航属性之延迟加载

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第五章 加载实体和导航属性 实体框架提供了非常棒的建模环境,它允许开发人员可视化地使 ...

  2. 《Entity Framework 6 Recipes》中文翻译系列 (28) ------ 第五章 加载实体和导航属性之测试实体是否加载与显式加载关联实体

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-11  测试实体引用或实体集合是否加载 问题 你想测试关联实体或实体集合是否已经 ...

  3. 《Entity Framework 6 Recipes》中文翻译系列 (23) -----第五章 加载实体和导航属性之预先加载与Find()方法

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-2  预先加载关联实体 问题 你想在一次数据交互中加载一个实体和与它相关联实体. ...

  4. 《Entity Framework 6 Recipes》中文翻译系列 (24) ------ 第五章 加载实体和导航属性之查询内存对象

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-4  查询内存对象 问题 你想使用模型中的实体对象,如果他们已经加载到上下文中, ...

  5. 《Entity Framework 6 Recipes》中文翻译系列 (25) ------ 第五章 加载实体和导航属性之加载完整的对象图和派生类型上的导航属性

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-5  加载完整的对象图 问题 你有一个包含许多关联实体的模型,你想在一次查询中, ...

  6. 《Entity Framework 6 Recipes》中文翻译系列 (26) ------ 第五章 加载实体和导航属性之延缓加载关联实体和在别的LINQ查询操作中使用Include()方法

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-7  在别的LINQ查询操作中使用Include()方法 问题 你有一个LINQ ...

  7. 《Entity Framework 6 Recipes》中文翻译系列 (29) ------ 第五章 加载实体和导航属性之过滤预先加载的实体集合和修改外键关联

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-13  过滤预先加载的实体集合 问题 你想过滤预先加载的实体集合,另外,你想使用 ...

  8. 《Entity Framework 6 Recipes》中文翻译系列 (31) ------ 第六章 继承与建模高级应用之自引用关联

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 6-4  使用TPH建模自引用关系 问题 你有一张自引用的表,它代表数据库上不同类型 ...

  9. 《Entity Framework 6 Recipes》中文翻译系列 (37) ------ 第六章 继承与建模高级应用之独立关联与外键关联

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 6-13  在基类中应用条件 问题 你想从一个已存在的模型中的实体派生一个新的实体, ...

随机推荐

  1. Android 6.0 - 动态权限管理的解决方案

    Android 6.0版本(Api 23)推出了很多新的特性, 大幅提升了用户体验, 同时也为程序员带来新的负担. 动态权限管理就是这样, 一方面让用户更加容易的控制自己的隐私, 一方面需要重新适配应 ...

  2. Linux SHELL 命令入门题目答案(一)

    1.如何使用shell 打印 “Hello World!” (1)如果你希望打印 !,那就不要将其放入双引号中,或者你可以通过转义字符转义(2)echo 'hello world!' 使用单引号ech ...

  3. css的padding

  4. 测试...外部指针访问private

    #include<iostream> using namespace std; class A{ public: int* getPointer(){ return &m; } v ...

  5. bzoj1008快速面

    快速面裸题(显然的m^n-m*(m-1)^(n-1)) 然后,,,就没有然后了 #include <cstdio> #define wzf2000 100003 long long n,m ...

  6. 【专业找水题】状压dp最水题,没有之一

    题目链接 现在代码能力没上升,倒是越来越会找水题了(比例题还水的裸题你值得拥有) 这网站不是针对竞赛的,所以时空限制都很宽松 然后就让我水过去了 对于每个点,包括自己的前m个元素是否取都是一种状态,所 ...

  7. Django--全文检索功能

    经过两个月的时间,毕设终于算是把所有主要功能都完成了,最近这一周为了实现全文检索的功能,也算是查阅了不少资料,今天就在这里记录一下,以免以后再用到时抓瞎了~ 首先介绍一下我使用的Django全文检索逻 ...

  8. Windows中断那些事儿

    搞内核研究的经常对中断这个概念肯定不陌生,经常我们会接触很多与中断相关的术语,按照软件和硬件进行分类: 硬件CPU相关: IRQ.IDT.cli&sti 软件操作系统相关: APC.DPC.I ...

  9. 基础3.Jquery操作Dom

                  1 内部插入节点 <body> <ul id="city"> <li id="bj" name=&qu ...

  10. bootstrap之伪元素

    bootstrap之伪元素 参考地址:http://www.cnblogs.com/keyi/p/5943178.html http://www.runoob.com/css/css-pseudo-e ...