《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中的模式正是使用这种方式。当在查询底层数据存储时,你也许更青睐LINQ-to-Entity。由于LINQ提供了许多特性以及强类型的编程体验。Entity SQL在通过实体数据模型,构建动态查询底层数据存储时提供了灵活性。

代码清单3-8. 使用Object Services和EntityClient执行一个Entity SQL语句

 1 using (var context = new EFRecipesEntities())
 2             {
 3                 // 删除测试数据
 4                 context.Database.ExecuteSqlCommand("delete from chapter3.customer");
 5                 // 添加新的测试数据
 6                 var cus1 = new Customer
 7                 {
 8                     Name = "Robert Stevens",
 9                     Email = "[email protected]"
10                 };
11                 var cus2 = new Customer
12                 {
13                     Name = "Julia Kerns",
14                     Email = "[email protected]"
15                 };
16                 var cus3 = new Customer
17                 {
18                     Name = "Nancy Whitrock",
19                     Email = "[email protected]"
20                 };
21                 context.Customers.Add(cus1);
22                 context.Customers.Add(cus2);
23                 context.Customers.Add(cus3);
24                 context.SaveChanges();
25             }
26
27             //使用ObjectContext对象中的 object services
28             using (var context = new EFRecipesEntities())
29             {
30                 Console.WriteLine("Querying Customers with eSQL Leveraging Object Services...");
31                 string esql = "select value c from Customers as c";
32                 // 将DbContext转换为底层的ObjectContext, 因为DbContext没有提供对Entity SQL查询的支持
33                 var customers = ((IObjectContextAdapter)context).ObjectContext.CreateQuery<Customer>(esql);
34                 foreach (var customer in customers)
35                 {
36                     Console.WriteLine("{0}‘s email is: {1}",
37                                        customer.Name, customer.Email);
38                 }
39             }
40
41             Console.WriteLine(System.Environment.NewLine);
42
43             //使用 EntityClient
44             using (var conn = new EntityConnection("name=EFRecipesEntities"))
45             {
46                 Console.WriteLine("Customers Customers with eSQL Leveraging Entity Client...");
47                 var cmd = conn.CreateCommand();
48                 conn.Open();
49                 cmd.CommandText = "select value c from EFRecipesEntities.Customers as c";
50                 using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
51                 {
52                     while (reader.Read())
53                     {
54                         Console.WriteLine("{0}‘s email is: {1}",
55                                            reader.GetString(1), reader.GetString(2));
56                     }
57                 }
58             }
59
60             Console.WriteLine("\nPress <enter> to continue...");
61             Console.ReadLine();
62         }

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

Querying Customers with eSQL Leveraging Object Services...
Robert Stevens‘s email is: [email protected]
Julia Kerns‘s email is: [email protected]
Nancy Whitrock‘s email is: [email protected]
Customers Customers with eSQL Leveraging Entity Client...
Robert Stevens‘s email is: [email protected]
Julia Kerns‘s email is: [email protected]
Nancy Whitrock‘s email is: [email protected]

  

原理

  有代码清单3-8中,一开始,我们删除了之前数据库中的测试数据。然后我们创建了三个customers,并将其添加到上下文对象中,并调用SaveChanges()将数据插入到数据库。

  使用数据库中的客户数据,我们演示了两种不同的,使用Entity SQL获取数据的方法。在第一种方法中,我们用CreateQuery()方法,该方法是在遗留的ObjectContext上下文对象中公布的,使用它创建一个ObjectQuery对象。 注意,我们是如何将DbContext转换成一个ObjectContextAdapter类型,并通过它得到底层的ObjectContext类型(记住,最新的DbContext包装了老的Objetcontext,以此改善开发者的编程体验)。我们这样做是因为DbContext不提供对 eSQL查询的直接支持。同时也需注意,我们使用占位符value代替Customer类型,然后将esql作为参数传递给CreateQuery()方法。当我们枚举customers集合时,查询在数据库被执行,同时,我们把结果集合输出到控制台。因为集合中的每个元素都是Customer实体类型的一个实例,所以,我们可以获得强类型的方式来使用每个元素的属性。

  在第二种方法中,我们使用EntityClinet库,它和我们使用SqlClient或者ADO.NET提供的别的Client相似。 先创建一个数据库连接,然后创建一个command对象,并打开数据库连接。接下来,我们用要执行的Entity SQL语句来初始化command对象。使用ExecuteReader()方法来执行command,并获得一个EntityDataReader,它与DbDataReader相似。最后,我们使用Read()方法枚举结果集。

  注意,在代码清单3-8中,Entity SQL语句使用的value关键字。 当我们需要获取完整的实体时,这个关键字非常有用。如果我们的Entity SQL 语句投影列的一个子集(也就是说,我们使用Entity SQL 表达式使用或创建部分列)我们无需使用value关键字。这意味着像代码清单3-9所演示的一样,直接使用DbDataRecord.

代码清单3-9. 使用Object Services 和EntityClient投影

 1  // 使用object ervices,无value关键字
 2             using (var context = new EFRecipesEntities())
 3             {
 4                 Console.WriteLine("Customers...");
 5                 string esql = "select c.Name, c.Email from Customers as c";
 6                 var records = ((IObjectContextAdapter)context).ObjectContext.CreateQuery<DbDataRecord>(esql);
 7                 foreach (var record in records)
 8                 {
 9                     var name = record[0] as string;
10                     var email = record[1] as string;
11                     Console.WriteLine("{0}‘s email is: {1}", name, email);
12                 }
13             }
14             Console.WriteLine();
15             //使用EntityClient,无value关键字
16             using (var conn = new EntityConnection("name=EFRecipesEntities"))
17             {
18                 Console.WriteLine("Customers...");
19                 var cmd = conn.CreateCommand();
20                 conn.Open();
21                 cmd.CommandText = @"select c.Name, C.Email from
22 EFRecipesEntities.Customers as c";
23                 using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
24                 {
25                     while (reader.Read())
26                     {
27                         Console.WriteLine("{0}‘s email is: {1}",
28                         reader.GetString(0), reader.GetString(1));
29                     }
30                 }
31             }

  当你使用Entity SQL 投影,返回的结果集是一个包含投影中的所有列的DbDataRecord。使用value关键字,查询返回的单独对象,是DbDataRecord中的第一个元素。

3-5 查找主从复合结构关系中的拥有从表记录的主表记录

问题
  你有两个一对多关联(主从复合结构关系)的实体。你要查询所有的至少拥有一个实体与它关联的实体。

解决方案

  假设你有一个拥有博客(BlogPost)和与之关联的评论(Comment)的模型。一些博客有很多评论,一些有少量或是没有评论。这个模型看起像图3-6。

图3-6 一个拥有博客(BlogPost)和与之关联的评论(Comment)的模型

  

  你要找出所有有评论的博客,可以使用LINQ to Entities 或者 Entity SQL。按代码清单3-10所演示的模式进行。

 1 using (var context = new EFRecipesEntities())
 2             {
 3                 // 删除测试数据
 4                 context.Database.ExecuteSqlCommand("delete from chapter3.comment");
 5                 context.Database.ExecuteSqlCommand("delete from chapter3.blogpost");
 6                 // 添加新的测试数据
 7                 var post1 = new BlogPost
 8                 {
 9                     Title = "The Joy of LINQ",
10                     Description = "101 things you always wanted to know about LINQ"
11                 };
12                 var post2 = new BlogPost
13                 {
14                     Title = "LINQ as Dinner Conversation",
15                     Description = "What wine goes with a Lambda expression?"
16                 };
17                 var post3 = new BlogPost
18                 {
19                     Title = "LINQ and our Children",
20                     Description = "Why we need to teach LINQ in High School"
21                 };
22                 var comment1 = new Comment
23                 {
24                     Comments = "Great post, I wish more people would talk about LINQ"
25                 };
26                 var comment2 = new Comment
27                 {
28                     Comments = "You‘re right, we should teach LINQ in high school!"
29                 };
30                 post1.Comments.Add(comment1);
31                 post3.Comments.Add(comment2);
32                 context.BlogPosts.Add(post1);
33                 context.BlogPosts.Add(post2);
34                 context.BlogPosts.Add(post3);
35                 context.SaveChanges();
36             }
37
38             using (var context = new EFRecipesEntities())
39             {
40                 Console.WriteLine("Blog Posts with comments...(LINQ)");
41                 var posts = from post in context.BlogPosts
42                     where post.Comments.Any()
43                     select post;
44                 foreach (var post in posts)
45                 {
46                     Console.WriteLine("Blog Post: {0}", post.Title);
47                     foreach (var comment in post.Comments)
48                     {
49                         Console.WriteLine("\t{0}", comment.Comments);
50                     }
51                 }
52             }
53
54             Console.WriteLine();
55
56             using (var context = new EFRecipesEntities())
57             {
58                 Console.WriteLine("Blog Posts with comments...(eSQL)");
59                 var esql = "select value p from BlogPosts as p where exists(p.Comments)";
60                 var posts = ((IObjectContextAdapter) context).ObjectContext.CreateQuery<BlogPost>(esql);
61                 foreach (var post in posts)
62                 {
63                     Console.WriteLine("Blog Post: {0}", post.Title);
64                     foreach (var comment in post.Comments)
65                     {
66                         Console.WriteLine("\t{0}", comment.Comments);
67                     }
68                 }
69             }
70
71             Console.WriteLine("\nPress <enter> to continue...");
72             Console.ReadLine();
73         }

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

 1 Blog Posts with comments...(LINQ)
 2 Blog Post: The Joy of LINQ
 3 Great post, I wish more people would talk about LINQ
 4 Blog Post: LINQ and our Children
 5 You‘re right, we should teach LINQ in high school!
 6 Blog Posts with comments...(ESQL)
 7 Blog Post: The Joy of LINQ
 8 Great post, I wish more people would talk about LINQ
 9 Blog Post: LINQ and our Children
10 You‘re right, we should teach LINQ in high school! 

原理

  在代码清单3-10中,我们先删除之前的测试数据,然后插入新的博客和评论到数据库,为了确保查询正确,我们让其中一篇博客没有任何评论。

  在LINQ查询中,我们在where从句中凭借LINQ扩展方法Any(),来判断给定的博客是否有评论。 查找所有Any()方法返回true的博客。在这种用法中,我们枚举Any()方法返回true的每一篇有评论的博客。而且,这正是我们所需要的:至少包含一个评论的博客。

  在Entity SQL 方法中,我们在where从句中使用了SQL exist()操作符,来判断给定的博客是否有评论。

  当然,我们还有别的方法也能获取到相同的结果。例如,我们可以在LINQ查询的where从句中使用Count()方法,来检查评论的数量是否大于0.在Entity SQL 方法中,我们可以在where从句中使用count(select value 1 from p.Comments)>0。这两种方法都可以正常运行,但是,代码清单3-10中的方法更加简洁,从性能的角度来看,Any()和Exist()不需要在服务器中枚举整个集合(意思是说,当找到第一个评论后,处理过程就开始转移到下一篇博客)。然而Count()需要在服务器中枚举整个集合(意思是说,尽管已经查到了一条评论了,仍然要枚举每一条评论)。

时间: 2024-12-18 19:11:30

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

《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》中文翻译系列 (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》翻译系列 (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