zoukankan      html  css  js  c++  java
  • 《Entity Framework 6 Recipes》中文翻译系列 (27) ------ 第五章 加载实体和导航属性之关联实体过滤、排序、执行聚合操作

    翻译的初衷以及为什么选择《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("
    Executive 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("	{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的输出如下:

    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

    原理

      代码清单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/

  • 相关阅读:
    这是一篇通过open live writer发布的博文
    网卡重启失败
    2020年1月目标
    二、安装docker
    JS中的数据类型,包含ES6,set和map等等
    关于prototype和__proto__,最好的一些解释
    JS中call,apply和bind方法的区别和使用场景
    ThinkPHP5生成word文档代码库
    js/jquery操作iframe
    PHP技术--思维导图
  • 原文地址:https://www.cnblogs.com/VolcanoCloud/p/4523563.html
Copyright © 2011-2022 走看看