《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 (var context = new EFRecipesEntities())
 2             {
 3                 // 删除之前的测试数据
 4                 context.Database.ExecuteSqlCommand("delete from chapter3.book");
 5                 context.Database.ExecuteSqlCommand("delete from chapter3.category");
 6                 // 添加新的测试数据
 7                 var cat1 = new Category {Name = "Programming"};
 8                 var cat2 = new Category {Name = "Databases"};
 9                 var cat3 = new Category {Name = "Operating Systems"};
10                 context.Books.Add(new Book {Title = "F# In Practice", Category = cat1});
11                 context.Books.Add(new Book {Title = "The Joy of SQL", Category = cat2});
12                 context.Books.Add(new Book
13                 {
14                     Title = "Windows 7: The Untold Story",
15                     Category = cat3
16                 });
17                 context.SaveChanges();
18             }
19
20             using (var context = new EFRecipesEntities())
21             {
22                 Console.WriteLine("Books (using LINQ)");
23                 var cats = new List<string> {"Programming", "Databases"};
24                 var books = from b in context.Books
25                     where cats.Contains(b.Category.Name)
26                     select b;
27                 foreach (var book in books)
28                 {
29                     Console.WriteLine("‘{0}‘ is in category: {1}", book.Title,
30                         book.Category.Name);
31                 }
32             }
33
34             using (var context = new EFRecipesEntities())
35             {
36                 Console.WriteLine("\nBooks (using eSQL)");
37                 var esql = @"select value b from Books as b
38                  where b.Category.Name in {‘Programming‘,‘Databases‘}";
39                 var books = ((IObjectContextAdapter) context).ObjectContext.CreateQuery<Book>(esql);
40                 foreach (var book in books)
41                 {
42                     Console.WriteLine("‘{0}‘ is in category: {1}", book.Title,
43                         book.Category.Name);
44                 }
45             }
46
47             Console.WriteLine("\nPress <enter> to continue...");
48             Console.ReadLine();
49         }

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

Books (using LINQ)
‘F# In Practice‘ is in category: Programming
‘The Joy of SQL‘ is in category: Databases
Books (using ESQL)
‘F# In Practice‘ is in category: Programming
‘The Joy of SQL‘ is in category: Databases

原理

  在LINQ查询中,我构造了一个简单的目录名列表,它使用LINQ查询操作符Contains。细心的读者可以注意了,我使用cats集合,并判断它是否包含某一目录名。实体框架将Containts从句转换成SQL语句中的in从句。如代码清单3-17所示:

代码清单3-17.  代码清单3-16中LINQ查询表达式对应的SQL语句

1 SELECT
2 [Extent1].[BookId] AS [BookId],
3 [Extent1].[Title] AS [Title],
4 [Extent1].[CategoryId] AS [CategoryId]
5 FROM [chapter3].[Books] AS [Extent1]
6 LEFT OUTER JOIN [chapter3].[Category] AS [Extent2] ON [Extent1].[CategoryId] = [Extent2].[CategoryId]
7 WHERE [Extent2].[Name] IN (N‘Programming‘,N‘Databases‘)

  有趣的是,代码清单3-17中的SQL语句,没有为从句的项使用参数。 这不同于LINQ to SQL中产生的代码,列表中的项会被参数化。超过SQL Server的参数限制的风险,会使代码不能运行。

  如果我们需要查找出给定目录列表中的所有书,列表目录中包含未分类的目录,我们只需在目录列表中简单地使用null值。产生的SQL语句,如代码清单3-18所示。

代码清单3-18.代码清单3-16中LINQ查询表达式对应的SQL语句,但是目录列表中多了一上null值。

1 SELECT
2 [Extent1].[BookId] AS [BookId],
3 [Extent1].[Title] AS [Title],
4 [Extent1].[CategoryId] AS [CategoryId]
5 FROM [chapter3].[Books] AS [Extent1]
6 LEFT OUTER JOIN [chapter3].[Category] AS [Extent2] ON [Extent1].[CategoryId] = [Extent2].[CategoryId]
7 WHERE [Extent2].[Name] IN (N‘Programming‘,N‘Databases‘)
8 OR [Extent2].[Name] IS NULL

  相同地,我们包含了一个使用Entity SQL的查询版本,它显式地包含了一个SQL IN 从句。

3-9过滤关联实体

问题

  你想获取一部分,不是全部关联实体。

解决方案

  假设你有如图3-10所示的模型。

图3-10 包含Worker和他们的Accidents的模型

  在模型中,一个Worker有零个或是多个accidents.每个事故按严重性分类,我们要获取所有的工人,对于与之关联的事故,只对严重事故感兴趣,事故的严重性大于2.

  在示例中我们使用了Code-First,在代码清单3-19中,创建了实体类Worker和Accidentd.

代码清单3-19. 实体类

 1  public class Worker
 2     {
 3         public Worker()
 4         {
 5             Accidents = new HashSet<Accident>();
 6         }
 7
 8         public int WorkerId { get; set; }
 9         public string Name { get; set; }
10
11         public virtual ICollection<Accident> Accidents { get; set; }
12     }
13
14
15  public class Accident
16     {
17         public int AccidentId { get; set; }
18         public string Description { get; set; }
19         public int? Severity { get; set; }
20         public int WorkerId { get; set; }
21
22         public virtual Worker Worker { get; set; }
23     }

  接下来,我们在代码清单3-20中创建Code-First中使用的上下文对象。

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

 1 public class EFRecipesEntities : DbContext
 2     {
 3         public EFRecipesEntities()
 4             : base("ConnectionString") {}
 5
 6         public DbSet<Accident> Accidents { get; set; }
 7         public DbSet<Worker> Workers { get; set; }
 8
 9         protected override void OnModelCreating(DbModelBuilder modelBuilder)
10         {
11             modelBuilder.Entity<Accident>().ToTable("Chapter3.Accident");
12             modelBuilder.Entity<Worker>().ToTable("Chapter3.Worker");
13             base.OnModelCreating(modelBuilder);
14         }
15     }

  使用代码清单3-21中的模式,获取所有的worders,和严重事故。

代码清单3-21 使用CreateSourceQuery和匿名类型获取严重事故

 1 using (var context = new EFRecipesEntities())
 2             {
 3                 // 删除之前的测试数据
 4                 context.Database.ExecuteSqlCommand("delete from chapter3.accident");
 5                 context.Database.ExecuteSqlCommand("delete from chapter3.worker");
 6                 // 添加新的测试数据
 7                 var worker1 = new Worker { Name = "John Kearney" };
 8                 var worker2 = new Worker { Name = "Nancy Roberts" };
 9                 var worker3 = new Worker { Name = "Karla Gibbons" };
10                 context.Accidents.Add(new Accident
11                 {
12                     Description = "Cuts and contusions",
13                     Severity = 3,
14                     Worker = worker1
15                 });
16                 context.Accidents.Add(new Accident
17                 {
18                     Description = "Broken foot",
19                     Severity = 4,
20                     Worker = worker1
21                 });
22                 context.Accidents.Add(new Accident
23                 {
24                     Description = "Fall, no injuries",
25                     Severity = 1,
26                     Worker = worker2
27                 });
28                 context.Accidents.Add(new Accident
29                 {
30                     Description = "Minor burn",
31                     Severity = 3,
32                     Worker = worker2
33                 });
34                 context.Accidents.Add(new Accident
35                 {
36                     Description = "Back strain",
37                     Severity = 2,
38                     Worker = worker3
39                 });
40                 context.SaveChanges();
41             }
42
43             using (var context = new EFRecipesEntities())
44             {
45                 // 显式禁用延迟加载
46                 context.Configuration.LazyLoadingEnabled = false;
47                 var query = from w in context.Workers
48                             select new
49                             {
50                                 Worker = w,
51                                 Accidents = w.Accidents.Where(a => a.Severity > 2)
52                             };
53                 query.ToList();
54                 var workers = query.Select(r => r.Worker);
55                 Console.WriteLine("Workers with serious accidents...");
56                 foreach (var worker in workers)
57                 {
58                     Console.WriteLine("{0} had the following accidents", worker.Name);
59                     if (worker.Accidents.Count == 0)
60                         Console.WriteLine("\t--None--");
61                     foreach (var accident in worker.Accidents)
62                     {
63                         Console.WriteLine("\t{0}, severity: {1}",
64                               accident.Description, accident.Severity.ToString());
65                     }
66                 }
67             }
68
69             Console.WriteLine("\nPress <enter> to continue...");
70             Console.ReadLine();
71         }

下面是代码清单3-21的输出:

Workers with serious accidents...
John Kearney had the following accidents
Cuts and contusions, severity: 3
Broken foot, severity: 4
Nancy Roberts had the following accidents
Minor burn, severity: 3
Karla Gibbons had the following accidents
--None--

原理

  正如你在随后的第五章中看到的那样,我们需要立即加载一个关联集合,我们经常使用Include()方法和一个查询路径。(Include()方法在单个查询中返回父对象和所有的子对象)。然后,Include()方法不允许过滤关联的子实体对象,在本节中,我们展示了,能过轻微的改变,让你可以加载并过滤相关联的子实体对象。

  在代码块中,我们创建了一些workers,并分配给它们相关的不同等级的accidents。不得不承认,分配事故给人,有点毛骨悚然!但,这只是为了得到一些可以上我们继续工作的数据。

  在随后的查询中,我们获取所有的工人并将结果投射到匿名对象上,这个类型包含了一个worker和一个accidets集合。对于accidents,应该注意,我们是如何过滤集合,只获取严重事故的。

  接下来的一行,非常重要!(译注:也就是query.ToList();),我们通过调用ToList()方法,强制查询求值。(记住,LINQ查询默认为延迟加载。意思是,直到结果被使用时,查询才被真正地执行。ToList()方法能强制查询执行)。在Dbcontext(译注:其实是它的子类EFRecipesEntities)中枚举所有的工人和所有的相关的严重事故。 匿名类型不会把accidents附加到workers上,但是通过把它们带到上下文中,实体框架会填充导航属性,将每一个严重事故集合accidents附加到合适的worker上。这个过程一般叫做:Entity Span。这是一个强大而微妙的,发生在实体框架实例化实体类型及它们之间关系的幕后的副作用。

  我们关闭了延迟加载,以便让accidents在我们的过滤查询中加载(我们将在第5章讨论延迟加载)。如果打开延迟加载,所有的accidents将在我们引用worker的accidents时才加载。这将导致过虑失败。

  我们一旦有了结果集合,就可以枚举并打印出每个worker和它的accidents信息。如果一个worker没有任何严重的accidents,我们打印none来指示他们的安全记录。

  

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

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

时间: 2024-08-06 07:59:25

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

《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》中文翻译系列 (17) -----第三章 查询之翻页、过滤和使用DateTime中的日期部分分组

翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-12 翻页和过滤 问题 你想使用分页和过滤来创建查询. 解决方案 假设你有如图3-13所示的模型,模型中有一个Custormer实体类型. 图3-13 包含一个Customer实体类型的模型 你有一个基于过滤条件来显示客户信息的应用.你的公司有许多客户(也许数百万!),为了保证尽可能响应的用户体验,你想在每一页上只显示一定数量的客户.创建一个查询,它能过虑客户并按页返回可控数量的结果集.

《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》中文翻译系列 (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》中文翻译系列 (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》中文翻译系列 (30) ------ 第六章 继承与建模高级应用之多对多关联

翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第六章  继承与建模高级应用 现在,你应该对实体框架中基本的建模有了一定的了解,本章将帮助你解决许多常见的.复杂的建模问题,并解决你可能在现实中遇到的建模问题. 本章以多对多关系开始,这个类型的关系,无论是在现存系统还是新项目的建模中都非常普遍.接下来,我们会了解自引用关系,并探索获取嵌套对象图的各种策略.最后,本章以继承的高级建模和实体条件结束. 6-1  获取多对多关联中的链接表 问题

《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