《Entity Framework 6 Recipes》中文翻译系列 (13) -----第三章 查询之使用Entity SQL
2015-05-17 11:39
525 查看
翻译的初衷以及为什么选择《Entity Framework 6 Recipes》来学习,请看本系列开篇
你想通过执行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语句
下面是代码清单3-8的输出:
原理
有代码清单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投影
当你使用Entity SQL 投影,返回的结果集是一个包含投影中的所有列的DbDataRecord。使用value关键字,查询返回的单独对象,是DbDataRecord中的第一个元素。
你有两个一对多关联(主从复合结构关系)的实体。你要查询所有的至少拥有一个实体与它关联的实体。
解决方案
假设你有一个拥有博客(BlogPost)和与之关联的评论(Comment)的模型。一些博客有很多评论,一些有少量或是没有评论。这个模型看起像图3-6。
图3-6 一个拥有博客(BlogPost)和与之关联的评论(Comment)的模型
你要找出所有有评论的博客,可以使用LINQ to Entities 或者 Entity SQL。按代码清单3-10所演示的模式进行。
下面是代码清单3-10的输出:
原理
在代码清单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()需要在服务器中枚举整个集合(意思是说,尽管已经查到了一条评论了,仍然要枚举每一条评论)。
实体框架交流QQ群: 458326058,欢迎有兴趣的朋友加入一起交流
谢谢大家的持续关注,我的博客地址:http://www.cnblogs.com/VolcanoCloud/
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语句
using (var context = new EFRecipesEntities()) { // 删除测试数据 context.Database.ExecuteSqlCommand("delete from chapter3.customer"); // 添加新的测试数据 var cus1 = new Customer { Name = "Robert Stevens", Email = "rstevens@mymail.com" }; var cus2 = new Customer { Name = "Julia Kerns", Email = "julia.kerns@abc.com" }; var cus3 = new Customer { Name = "Nancy Whitrock", Email = "nrock@myworld.com" }; context.Customers.Add(cus1); context.Customers.Add(cus2); context.Customers.Add(cus3); context.SaveChanges(); } //使用ObjectContext对象中的 object services using (var context = new EFRecipesEntities()) { Console.WriteLine("Querying Customers with eSQL Leveraging Object Services..."); string esql = "select value c from Customers as c"; // 将DbContext转换为底层的ObjectContext, 因为DbContext没有提供对Entity SQL查询的支持 var customers = ((IObjectContextAdapter)context).ObjectContext.CreateQuery<Customer>(esql); foreach (var customer in customers) { Console.WriteLine("{0}'s email is: {1}", customer.Name, customer.Email); } } Console.WriteLine(System.Environment.NewLine); //使用 EntityClient using (var conn = new EntityConnection("name=EFRecipesEntities")) { Console.WriteLine("Customers Customers with eSQL Leveraging Entity Client..."); var cmd = conn.CreateCommand(); conn.Open(); cmd.CommandText = "select value c from EFRecipesEntities.Customers as c"; using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess)) { while (reader.Read()) { Console.WriteLine("{0}'s email is: {1}", reader.GetString(1), reader.GetString(2)); } } } Console.WriteLine("\nPress <enter> to continue..."); Console.ReadLine(); }
下面是代码清单3-8的输出:
Querying Customers with eSQL Leveraging Object Services... Robert Stevens's email is: rstevens@mymail.com Julia Kerns's email is: julia.kerns@abc.com Nancy Whitrock's email is: nrock@myworld.com Customers Customers with eSQL Leveraging Entity Client... Robert Stevens's email is: rstevens@mymail.com Julia Kerns's email is: julia.kerns@abc.com Nancy Whitrock's email is: nrock@myworld.com
原理
有代码清单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投影
// 使用object ervices,无value关键字 using (var context = new EFRecipesEntities()) { Console.WriteLine("Customers..."); string esql = "select c.Name, c.Email from Customers as c"; var records = ((IObjectContextAdapter)context).ObjectContext.CreateQuery<DbDataRecord>(esql); foreach (var record in records) { var name = record[0] as string; var email = record[1] as string; Console.WriteLine("{0}'s email is: {1}", name, email); } } Console.WriteLine(); //使用EntityClient,无value关键字 using (var conn = new EntityConnection("name=EFRecipesEntities")) { Console.WriteLine("Customers..."); var cmd = conn.CreateCommand(); conn.Open(); cmd.CommandText = @"select c.Name, C.Email from EFRecipesEntities.Customers as c"; using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess)) { while (reader.Read()) { Console.WriteLine("{0}'s email is: {1}", reader.GetString(0), reader.GetString(1)); } } }
当你使用Entity SQL 投影,返回的结果集是一个包含投影中的所有列的DbDataRecord。使用value关键字,查询返回的单独对象,是DbDataRecord中的第一个元素。
3-5 查找主从复合结构关系中的拥有从表记录的主表记录
问题你有两个一对多关联(主从复合结构关系)的实体。你要查询所有的至少拥有一个实体与它关联的实体。
解决方案
假设你有一个拥有博客(BlogPost)和与之关联的评论(Comment)的模型。一些博客有很多评论,一些有少量或是没有评论。这个模型看起像图3-6。
图3-6 一个拥有博客(BlogPost)和与之关联的评论(Comment)的模型
你要找出所有有评论的博客,可以使用LINQ to Entities 或者 Entity SQL。按代码清单3-10所演示的模式进行。
using (var context = new EFRecipesEntities()) { // 删除测试数据 context.Database.ExecuteSqlCommand("delete from chapter3.comment"); context.Database.ExecuteSqlCommand("delete from chapter3.blogpost"); // 添加新的测试数据 var post1 = new BlogPost { Title = "The Joy of LINQ", Description = "101 things you always wanted to know about LINQ" }; var post2 = new BlogPost { Title = "LINQ as Dinner Conversation", Description = "What wine goes with a Lambda expression?" }; var post3 = new BlogPost { Title = "LINQ and our Children", Description = "Why we need to teach LINQ in High School" }; var comment1 = new Comment { Comments = "Great post, I wish more people would talk about LINQ" }; var comment2 = new Comment { Comments = "You're right, we should teach LINQ in high school!" }; post1.Comments.Add(comment1); post3.Comments.Add(comment2); context.BlogPosts.Add(post1); context.BlogPosts.Add(post2); context.BlogPosts.Add(post3); context.SaveChanges(); } using (var context = new EFRecipesEntities()) { Console.WriteLine("Blog Posts with comments...(LINQ)"); var posts = from post in context.BlogPosts where post.Comments.Any() select post; foreach (var post in posts) { Console.WriteLine("Blog Post: {0}", post.Title); foreach (var comment in post.Comments) { Console.WriteLine("\t{0}", comment.Comments); } } } Console.WriteLine(); using (var context = new EFRecipesEntities()) { Console.WriteLine("Blog Posts with comments...(eSQL)"); var esql = "select value p from BlogPosts as p where exists(p.Comments)"; var posts = ((IObjectContextAdapter) context).ObjectContext.CreateQuery<BlogPost>(esql); foreach (var post in posts) { Console.WriteLine("Blog Post: {0}", post.Title); foreach (var comment in post.Comments) { Console.WriteLine("\t{0}", comment.Comments); } } } Console.WriteLine("\nPress <enter> to continue..."); Console.ReadLine(); }
下面是代码清单3-10的输出:
Blog Posts with comments...(LINQ) Blog Post: The Joy of LINQ Great post, I wish more people would talk about LINQ Blog Post: LINQ and our Children You're right, we should teach LINQ in high school! Blog Posts with comments...(ESQL) Blog Post: The Joy of LINQ Great post, I wish more people would talk about LINQ Blog Post: LINQ and our Children 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()需要在服务器中枚举整个集合(意思是说,尽管已经查到了一条评论了,仍然要枚举每一条评论)。
实体框架交流QQ群: 458326058,欢迎有兴趣的朋友加入一起交流
谢谢大家的持续关注,我的博客地址:http://www.cnblogs.com/VolcanoCloud/
相关文章推荐
- 《Entity Framework 6 Recipes》中文翻译系列 (17) -----第三章 查询之分页、过滤和使用DateTime中的日期部分分组
- 《Entity Framework 6 Recipes》中文翻译系列 (19) -----第三章 查询之使用位操作和多属性连接(join)
- 《Entity Framework 6 Recipes》中文翻译系列 (14) -----第三章 查询之查询中设置默认值和存储过程返回多结果集
- 《Entity Framework 6 Recipes》中文翻译系列 (15) -----第三章 查询之与列表值比较和过滤关联实体
- 《Entity Framework 6 Recipes》中文翻译系列 (16) -----第三章 查询之左连接和在TPH中通过派生类排序
- 《Entity Framework 6 Recipes》中文翻译系列 (18) -----第三章 查询之结果集扁平化和多属性分组
- 《Entity Framework 6 Recipes》中文翻译系列 (21) -----第四章 ASP.NET MVC中使用实体框架之在页面中创建查询和使用ASP.NET URL路由过虑
- 《Entity Framework 6 Recipes》中文翻译系列 (26) ------ 第五章 加载实体和导航属性之延缓加载关联实体和在别的LINQ查询操作中使用Include()方法
- 《Entity Framework 6 Recipes》中文翻译系列 (40) ------ 第七章 使用对象服务之从跟踪器中获取实体与从命令行生成模型(想解决EF第一次查询慢的,请阅读)
- 《Entity Framework 6 Recipes》中文翻译系列 (41) ------ 第七章 使用对象服务之标识关系中使用依赖实体与异步查询保存
- 《Entity Framework 6 Recipes》中文翻译系列 (20) -----第四章 ASP.NET MVC中使用实体框架之在MVC中构建一个CRUD示例
- 《Entity Framework 6 Recipes》中文翻译系列 (24) ------ 第五章 加载实体和导航属性之查询内存对象
- 《Entity Framework 6 Recipes》中文翻译系列 (35) ------ 第六章 继承与建模高级应用之TPH继承映射中使用复合条件
- 《Entity Framework 6 Recipes》中文翻译系列 (38) ------ 第七章 使用对象服务之动态创建连接字符串和从数据库读取模型
- 《Entity Framework 6 Recipes》中文翻译系列 (39) ------ 第七章 使用对象服务之配置模型和使用单复数服务
- 《Entity Framework 6 Recipes》中文翻译系列 (42) ------ 第八章 POCO之使用POCO
- 《Entity Framework 6 Recipes》中文翻译系列 (43) ------ 第八章 POCO之使用POCO加载实体
- 《Entity Framework 6 Recipes》中文翻译系列 (44) ------ 第八章 POCO之POCO中使用值对象和对象变更通知
- 医学开源配准软件Elastix手册(中文翻译)——第三章 Elastix安装和使用
- 《Entity Framework 6 Recipes》中文翻译系列 目录篇 -持续更新