• 《Entity Framework 6 Recipes》中文翻译系列 (17) 第三章 查询之分页、过滤和使用DateTime中的日期部分分组


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

    3-12 分页和过滤

    问题

      你想使用分页和过滤来创建查询。

    解决方案

      假设你有如图3-13所示的模型,模型中有一个Custormer实体类型。

    图3-13 包含一个Customer实体类型的模型

      你有一个基于过滤条件来显示客户信息的应用。你的公司有许多客户(也许数百万!),为了保证尽可能响应的用户体验,你想在每一页上只显示一定数量的客户。创建一个查询,它能过虑客户并按页返回可控数量的结果集。如代码清单3-26所示。

    代码清单3-26. 一个包含过滤和分页的查询

     1 using (var context = new EFRecipesEntities())
     2             {
     3                 // 删除之前的数据
     4                 context.Database.ExecuteSqlCommand("delete from chapter3.customer");
     5                 // 添加新的测试数据
     6                 context.Customers.Add(new Customer
     7                 {
     8                     Name = "Roberts, Jill",
     9                     Email = "jroberts@abc.com"
    10                 });
    11                 context.Customers.Add(new Customer
    12                 {
    13                     Name = "Robertson, Alice",
    14                     Email = "arob@gmail.com"
    15                 });
    16                 context.Customers.Add(new Customer
    17                 {
    18                     Name = "Rogers, Steven",
    19                     Email = "srogers@termite.com"
    20                 });
    21                 context.Customers.Add(new Customer
    22                 {
    23                     Name = "Roe, Allen",
    24                     Email = "allenr@umc.com"
    25                 });
    26                 context.Customers.Add(new Customer
    27                 {
    28                     Name = "Jones, Chris",
    29                     Email = "cjones@ibp.com"
    30                 });
    31                 context.SaveChanges();
    32             }
    33 
    34 
    35             using (var context = new EFRecipesEntities())
    36             {
    37                 string match = "Ro";
    38                 int pageIndex = 0;
    39                 int pageSize = 3;
    40 
    41                 var customers = context.Customers.Where(c => c.Name.StartsWith(match))
    42                 //var customers = context.Customers.Where(c => c.Name.Contains(match))
    43                                     .OrderBy(c => c.Name)
    44                                     .Skip(pageIndex * pageSize)
    45                                     .Take(pageSize);
    46                 Console.WriteLine("Customers Ro*");
    47                 foreach (var customer in customers)
    48                 {
    49                     Console.WriteLine("{0} [email: {1}]", customer.Name, customer.Email);
    50                 }
    51             }
    52 
    53             using (var context = new EFRecipesEntities())
    54             {
    55                 string match = "Ro%";
    56                 int pageIndex = 0;
    57                 int pageSize = 3;
    58 
    59                 var esql = @"select value c from Customers as c 
    60                  where c.Name like @Name
    61                  order by c.Name
    62                  skip @Skip limit @Limit";
    63                 Console.WriteLine("\nCustomers Ro*");
    64                 var customers = ((IObjectContextAdapter)context).ObjectContext.CreateQuery<Customer>(esql, new[]
    65                       {
    66                         new ObjectParameter("Name",match),
    67                         new ObjectParameter("Skip",pageIndex * pageSize),
    68                         new ObjectParameter("Limit",pageSize)
    69                       });
    70                 foreach (var customer in customers)
    71                 {
    72                     Console.WriteLine("{0} [email: {1}]", customer.Name, customer.Email);
    73                 }
    74             }
    75 
    76             Console.WriteLine("\nPress <enter> to continue...");
    77             Console.ReadLine();

    代码清单3-26的输出如下:

    Customers Ro*
    Roberts, Jill [email: jroberts@abc.com]
    Robertson, Alice [email: arob@gmail.com]
    Roe, Allen [email: allenr@umc.com]
    Customers Ro*
    Roberts, Jill [email: jroberts@abc.com]
    Robertson, Alice [email: arob@gmail.com]
    Roe, Allen [email: allenr@umc.com]

    原理

      在代码清单3-26中,针对这个问题,我们展示了不同的方法。在第一种方法中,我们使用了LINQ to Entities扩展方法创建了一个LINQ查询。我们使用Where()方法过滤结果集,过虑条件为,姓以“Ro“开头。因为我们在lambda表达工中使用了扩展方法StartsWith()。我们不需要使用SQL的通配符表达式“Ro%"。

      过滤后,我们使用OrderBy()方法对结果集排序,排序后的结果集通过方法Skip()来获取。我们使用Skip()方法跳过PageIndex页,每页的大小为PageSize. 使用Take()方法来获取受限的结果集(译注:从结果集获取指定页大小的记录数),我们只需要获取结果集中的一页。

      注意,在代码块中,我们使用LINQ扩展方法创建了一个完整的查询,而不是我们之前看到的SQL查询表达式。Skip()和Take()方法只在扩展方法中公布,不是查询语法。

      第二种方法,我们构建了一个完整的参数化的Entity SQL表达式,这也许是你最熟悉的方式,但是这在使用字符串和可执行代码(C#)来表示查询的两种方式间产生了固有的不匹配风险。

    3-13 按日期分组

    问题

      你有一个包含DateTime类型属性的实体,你想通过DateTime类型属性的Date部分来对实体的实例进行分组。

    解决方案

      假设你有如图3-14所示的模型,模型中有一个Registration实体类型,该实体类型包含一个DateTime类型的属性。

    图3-14 模型中有一个Registration实体类型,该实体类型包含一个DateTime类型的属性

      该示例使用Code-First方法,在代码清单3-27中,我们创建了一些实体。

    代码清单3-27. Registration实体类型

    1 public class Registration
    2 {
    3     public int RegistrationId { get; set; }
    4     public string StudentName { get; set; }
    5     public DateTime? RegistrationDate { get; set; }
    6 }

      接下来,代码清单3-28中创建了上下文对象,它是用Code-First方法访问实体框架功能的入口。

    代码清单3-28.  上下文对象

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

      我们使用RegistrationDate属性的Date部分对所有的registrations进行分组,你可能会对LINQ中的 group by RegistrationDate.Date动心.虽然能过编译,但是你仍会得到一个运行时错误,该错误描述Date不能转换成SQL。 为了能使用RegistrationDate属性的Date部分来分组,请看代码清单3-29.

    代码清单3-29. 使用DateTime类型属性的Date部分对实例进行分组

     1 using (var context = new EFRecipesEntities())
     2             {
     3                 // 删除之前的测试数据
     4                 context.Database.ExecuteSqlCommand("delete from chapter3.registration");
     5                 // 添加新的测试数据
     6                 context.Registrations.Add(new Registration
     7                 {
     8                     StudentName = "Jill Rogers",
     9                     RegistrationDate = DateTime.Parse("12/03/2009 9:30 pm")
    10                 });
    11                 context.Registrations.Add(new Registration
    12                 {
    13                     StudentName = "Steven Combs",
    14                     RegistrationDate = DateTime.Parse("12/03/2009 10:45 am")
    15                 });
    16                 context.Registrations.Add(new Registration
    17                 {
    18                     StudentName = "Robin Rosen",
    19                     RegistrationDate = DateTime.Parse("12/04/2009 11:18 am")
    20                 });
    21                 context.Registrations.Add(new Registration
    22                 {
    23                     StudentName = "Allen Smith",
    24                     RegistrationDate = DateTime.Parse("12/04/2009 3:31 pm")
    25                 });
    26                 context.SaveChanges();
    27             }
    28 
    29             using (var context = new EFRecipesEntities())
    30             {
    31                 var groups = from r in context.Registrations
    32                              // 凭借内置的TruncateTime函数提取Date部分
    33                              group r by DbFunctions.TruncateTime(r.RegistrationDate)
    34                                  into g
    35                                  select g;
    36                 foreach (var element in groups)
    37                 {
    38                     Console.WriteLine("\nRegistrations for {0}",
    39                            ((DateTime)element.Key).ToShortDateString());
    40                     foreach (var registration in element)
    41                     {
    42                         Console.WriteLine("\t{0}", registration.StudentName);
    43                     }
    44                 }
    45             }
    46 
    47             Console.WriteLine("\nPress <enter> to continue...");
    48             Console.ReadLine();

    代码清单3-29输出如下:

    Registrations for 12/3/2009
    Jill Rogers
    Steven Combs
    Registrations for 12/4/2009
    Robin Rosen
    Allen Smit

    原理

      对registrations分组的分组键是通过Truncate()函数提RegistrationDate属性中的Date部分。这是实体框架的内置函数,包含在DbFunctions类中,它只提取DateTime中的Date部分。内置的DbFunctions类,包含着大量的,格式化、聚合、字符串操作、日期和数字服务。它在命名空间System.Data.Entity中。遗留类,EntityFunctios,是在EF6之前的版本中使用的,它仍然能在EF6中使用,但你会得到一个编译警告,它建议你使用最亲的DbFunctions类。我们将在11章继续讨论它。

    实体框架交流QQ群:  458326058,欢迎有兴趣的朋友加入一起交流

    谢谢大家的持续关注,我的博客地址:http://www.cnblogs.com/VolcanoCloud/

    版权所有,转载请注明出处 付灿的技术博客
  • 相关阅读:
    BFS visit tree
    Kth Largest Element in an Array 解答
    Merge k Sorted Lists 解答
    Median of Two Sorted Arrays 解答
    Maximal Square 解答
    Best Time to Buy and Sell Stock III 解答
    Best Time to Buy and Sell Stock II 解答
    Best Time to Buy and Sell Stock 解答
    Triangle 解答
    Unique Binary Search Trees II 解答
  • 原文地址:https://www.cnblogs.com/VolcanoCloud/p/4513791.html
Copyright © 2020-2023  润新知