《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 = "[email protected]"
10                 });
11                 context.Customers.Add(new Customer
12                 {
13                     Name = "Robertson, Alice",
14                     Email = "[email protected]"
15                 });
16                 context.Customers.Add(new Customer
17                 {
18                     Name = "Rogers, Steven",
19                     Email = "[email protected]"
20                 });
21                 context.Customers.Add(new Customer
22                 {
23                     Name = "Roe, Allen",
24                     Email = "[email protected]"
25                 });
26                 context.Customers.Add(new Customer
27                 {
28                     Name = "Jones, Chris",
29                     Email = "[email protected]"
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: [email protected]]
Robertson, Alice [email: [email protected]]
Roe, Allen [email: [email protected]]
Customers Ro*
Roberts, Jill [email: [email protected]]
Robertson, Alice [email: [email protected]]
Roe, Allen [email: [email protected]]

原理

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

时间: 2024-11-05 20:45:20

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

《Entity Framework 6 Recipes》中文翻译系列 (18) -----第三章 查询之结果集扁平化和多属性分组

翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-14  结果集扁平化 问题 你有一对多关联的两个实体,你想通过一个查询,获取关联中的两个实体的扁平化投影.扁平化或者叫压缩,这是不规范的叫法.它是指一个有父类和子类的对象图,被投影到一个单独的类中. 解决方案 假设你有一对拥有一对多关联的实体,如图3-15所示的模型. 图3-15 模型中,一个代表助理的Associate的实体类型和一个代表助理工资历史的AssociateSalary实体

《Entity Framework 6 Recipes》中文翻译系列 (14) -----第三章 查询之查询中设置默认值和存储过程返回多结果集 (转)

翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-6在查询中设置默认值 问题 你有这样一个用例,当查询返回null值时,给相应属性设置默认值.在我们示例中,当数据库中返回null值时,用‘0’作为YearsWorked属性的默认值. 解决方案 假设你有如图3-7所示的模型,你想通过模型查询employees.在数据库中,代表employees的表包含一可为空的YearsWorked列.该列映射到Employee实体中的YearsWork

《Entity Framework 6 Recipes》中文翻译系列 (15) -----第三章 查询之与列表值比较和过滤关联实体

翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-8与列表值比较 问题 你想查询一个实体,条件是给定的列表中包含指定属性的值. 解决方案 假设你有如图3-9所示的模型. 图3-9 包含books和它的categoryes的模型 你想查找给定目录列表中的所有图书.在代码清单3-16中使用LINQ和Entity SQL来实现这上功能. 代理清单3-16. 使用LINQ和Entity SQL来查找给定目录列表中的所有图书 1 using (v

《Entity Framework 6 Recipes》中文翻译系列 (16) -----第三章 查询之左连接和在TPH中通过派生类排序

翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-10应用左连接 问题 你想使用左外连接来合并两个实体的属性. 解决方案 假设你有如图3-11所示的模型. 图3-11 一个包含Product和TopSelling的模型 畅销产品有一个与之关联的TopSelling实体.当然,不是所有的产品都是畅销产品.这就是为什么关系为零或者1.当一个产品为畅销产品时,与之关联的topSelling实体包含一个用户评级.你想查找和呈现所有的产品,和与之

《Entity Framework 6 Recipes》中文翻译系列 (13) -----第三章 查询之使用Entity SQL

3-4使用实体SQL查询模型 问题 你想通过执行Entity SQL语句来查询你的实体数据模型并返回强类型的对象. 解决方案 假设你有图3-5所示的模型,它包含一个Customer实体类型.这个实体类型有一个Name属性和Email属性.你要使用Entiyt SQL查询这个模型. 图3-5 包含一个Customer实体类型的模型 使用Entity SQL(eSQL)查询模型,Entity SQL是SQL在实体框架中实现的一种方言,代码清单3-8中的模式正是使用这种方式.当在查询底层数据存储时,你

《Entity Framework 6 Recipes》中文翻译系列 (12) -----第三章 查询之使用SQL语句 (转)

3-2使用原生SQL语句更新 问题 你想在实体框架中使用原生的SQL语句,来更新底层数据存储. 解决方案 假设你有一张如图3-2所示的Payment数据库表,使用实体框架设计器工具创建了一个如图3-2所示的模型. 图3-2 Payment表,包含一个供应商的付款信息 图3-3 包含一个Payment实体的模型 为了在底层的Payment表中执行一句和多句SQL语句,可以使用在DbContext类中的属性Database中的ExecuteSQlCommand()方法. 虽然我们能在模型中查询Pay

《Entity Framework 6 Recipes》翻译系列 (1) -----第一章 开始使用实体框架之历史和框架简述 (转)

微软的Entity Framework 受到越来越多人的关注和使用,Entity Framework7.0版本也即将发行.虽然已经开源,可遗憾的是,国内没有关于它的书籍,更不用说好书了,可能是因为EF版本更新太快,没人愿意去花时间翻译国外关于EF的书籍.使用Entity Framework开发已经有3年多了,但用得很肤浅,最近想深入学习,只好找来英文书<Entity Framework 6 Recipes>第二版,慢慢啃.首先需要说明的是,我英文不好,只是为了学习EF.把学习的过程写成博客,一

《Entity Framework 6 Recipes》翻译系列(2) -----第一章 开始使用实体框架之使用介绍 (转)

Visual Studio 我们在Windows平台上开发应用程序使用的工具主要是Visual Studio.这个集成开发环境已经演化了很多年,从一个简单的C++编辑器和编译器到一个高度集成.支持软件开发整个生命周期的多语言环境. Visual Studio以及它发布的工具和服务提供了:设计.开发.单元测试.调试.软件配置和管理.构建管理和持续集成等等.很少有开发人员因为还没有使用它而担心(注:作者应该是表达不用担心VS的能力),Visual Studio是一个完整的工具集.Visual Stu

《Entity Framework 6 Recipes》翻译系列 2 -----第一章 开始使用实体框架2

Visual Studio 我们在Windows平台上开发应用程序使用的工具主要是Visual Studio.这个集成开发环境已经演化了很多年,从一个简单的C++编辑器和编译器到一个高度集成.支持软件开发整个生命周期的多语言环境. Visual Studio以及它发布的工具和服务提供了:设计.开发.单元测试.调试.软件配置和管理.构建管理和持续集成等等.很少有开发人员因为还没有使用它而担心(注:作者应该是表达不用担心VS的能力),Visual Studio是一个完整的工具集.Visual Stu