你可以以多种方式查询实体数据模型。有些你可以根据个人偏好选择,而有些选择可以给你带来一定好处。或许你听说过LINQ to Entities和实体SQL.你也可以使用特殊的方法来表示查询(有些是基于LINQ,而一些是基于实体框架的ObjectQuery类)。每种查询风格都可以得到组织过的对象。有一个不太为人所知的查询方式是使用实体框架的EntityClient API,它可以让你在程序里使用流式的原始数据。
在本章,你有机会尝试下所有这些不同风格的查询。你会使用不同的机制反复书写几个简单的查询并查看结果,以便可以看到这些不同的查询方法彼此之间如何相关的。
在本章的末尾,你就能够对所有的查询选项以及它们的基本使用有个更高的理解。在之后的章节里,你就可以书写更复杂的查询;你从本章学到的基础就可以让你更容易办到。另外,在本章最后,你可以找到关于查询执行的很关键的一课。
尽管本章的查询样例都是以控制台应用程序的方式存在,但是你可以使用LINQPad来测试这些查询并看到结果。本章一些指南里也会审查调试器,这个你在LINQPad是不能办到的。参见第56页中的LINQPad一栏来了解关于该工具的更多信息。
查询模型,而不是数据库
在本章里,你将学到如何构建基于第2章里创建的EDM来构建查询,你会学到让实体框架那样来。在这里你就会体验到针对一个数据查询书写查询与针对数据库两者的差别。实体框架会处理你的查询,并利于ADO.NET提供程序(这个例子里是System.Data.SqlClient)把EDM查询转换成目标数据库所理解的查询。在数据库执行了查询以后,结果会被转化成基于该模型中的实体的对象。
这些返回的对象是查询过程中很重要的一块儿,但你一定想要开始查询了,所以首先我们查询然后我们在分章节来细看。
你的第一个EDM查询
在第2章里,你在一个控制台程序里创建了一个EDM。这里你要创建第一个针对此EDM的查询。你可以用同一个项目,所以要是你已经把它给关了,请打开它让我们开始吧。这一节的代码会执行最简单形式的一个查询,它会从数据库返回每个Contact实体,然后在控制台窗口里显示结果。
1.打开Program.cs文件。
2.在Main方法下面增加一个例3-1l里的方法。智能提示可以在你输入的时候帮助你。在书写了几个基本的查询之后,你的代码看上去更像那么回事了。
例3-1 查询Contact并输出它们的名字
1 private static void QueryContacts() 2 3 { 4 5 using (var context = new SampleEntities()) 6 7 { 8 9 var contacts = context.Contacts; 10 11 foreach (var contact in contacts) 12 13 { 14 15 Console.WriteLine("{0} {1}", 16 17 contact.FirstName.Trim(), 18 19 contact.LastName); 20 21 } 22 23 } 24 25 Console.Write("Press Enter..."); 26 27 Console.ReadLine(); 28 29 }
3.在Main方法里增加下面的代码:
QueryContacts();
4.按F5运行这点代码。当代码执行到Readline()方法这里,所有的名字都列在了控制台窗口里。
你刚刚执行了你的第一个EDM查询,也看到了生成的结果。
5.按Enter键结束该程序的运行。
现在你要再运行一次这个查询,但是这一次你要看下具体的执行细节:
1.在foreach块结束地方设置一个断点,就是关闭大括号的地方(})。
2.按F5再运行下该代码。
3.当调试器执行到该断点的地方,把鼠标指针放在foreach语句里的contact变量上面,你会看到它是Contact实体类型的(见图3-1)。
图3-1 运行时返回Contact实体的查询结果
4.接下来,把你的鼠标指针放到同一条语句的contacts变量上面,你可以看到它的类型是Contact类型的System.Data.Objects.ObjectSet。
System.Data.Objects是实体框架API用于创建和管理实体对象的。每当你调用一个EntitySet(例如Contacts)时,实体框架返回ObjectSet。它派生自另外一个重要的类ObjectQuery(它用来创建和执行返回对象的查询).一旦执行ObjectQuery,它包含的结果里就是你在控制台里看到的那些contact。上下文携带返回的数据并使用它们为你创建这些Contact对象。
因为你只要求这些Contact而没有请求任何的过滤,所以当查询执行时会从数据库检索所有的联系人。
虽然这看起来真不像个查询,但是它就是一个查询---即便是很简单一个。你会在下一条查询之后仔细看下这个。
5.你可以继续执行该程序或者干脆按Shift+F5停止。
既然你知道这个查询返回一个ObjectSet了,你可以把使用var关键字模糊类型的这段代码改写成明确的声明类型。这种方式下,你可以把代码(比如context.Contacts)里返回类型不明显的地方指定下类型,这样对你还是他人来说在以后也能更容易理解你的代码。
ObjectSet<Contact> contacts=context.Contacts;
注:ObjectSet位于System.Data.Objects名称空间中。你或者可以在代码行里指定或者在你的代码文件开始的地方添加该名称空间(使用using System.Data.Objects或者对VB的可以用Imports System.Data.Objects)。
Context(上下文)与Classes(类)从何而来
既然刚刚你也深入了代码,或许你有几个疑问。例如,Contact类型从何而来?你又是如何从一个XML文件(EDMX文件)转到强类型的.NET对象的?context.Contacts为什么是查本身,而又那个上下文到底是什么?
EDM设计工具其中一个特性就是设计器可以自动基于模型生成代码。在解决方案管理器里Designer.cs文件依附到该模型,如下图3-2所示.
图3-2 在解决方案管理器里自动产生的代码文件依附在该模型上
在解决方案管理器里展开.edmx文件查看这个产生的代码文件。打开该文件看看都有什么。
注:因为这个文件是自动产生的,所以你不需要直接编辑它。在第11章里你将了解到如何定制这个文件里面的类。
生成器读取该模型的概念层,然后从其创建一个基于EntityContainer的ObjectContext类,以及模型中每个实体的实体类。(见图3-3)
图3-3 基于概念模型产生的ObjectContext和实体类
产生的代码文件包含四个类。图3-4在Visual Studio的类设计器视图里显示了这些类。你可以通过在解决方案管理器里单击该类,然后选择查看类图在类设计器里打开一个类。
第一个类(我通过单击右上角的箭头已经展开了该类而不是显示它的默认视图)是SampleEntities.这个类采用了模型的EntityContainer名称。其它的是每个实体类—Address,Contact和vOfficeAddress.
图3-4 Visual Studio类设计器视图中的这四个类
ObjectContext类—SampleEntities
当你在第2章中查看模型的XML视图时,你看到了EntityContainer包含有EntitySet和AssociationSet.
SampleEntities类代表那个EntityContainer,它派生自实体框架的ObjectContext类型。这就是为什么例子中选用context作为变量的原因。SampleEntities有3个属性—Addresses、Contacts和vOfficeAddresses—它们是模型中定义的EntitySet.代码生成器创建了3个AddTo方法用于提供一种方式来增加新的实例对象到上下文中,进而能够把它们插入数据库中。这些AddTo方法存在是为了向后兼容.NET3.5版本的实体框架。在.NET4中,你应该利用ObjectSet所提供的Add方法,你将在后面的章节了解到这个。
注:我在实体框架编码中有个习惯就是总是用context作为ObjectContext示例的变量名。
仔细观察Contacts属性,你能看到它返回一个Contact类型的ObjectSet:
public ObjectSet<Contact> Contacts
注:对于VB开发人员来说,如果你不熟悉泛型的语法,C#是在尖括号里表示类型而VB使用的是圆括号和关键字Of.前面这个代码如果用VB来写将如下所示:
Public Property Contacts As ObjectSet(Of Contact)
ObjectSet是我们查询的基础,不管你是想要整个Contact实体集,比如像例3-1里请求的那样,还是想要一个子集,这个我们会在例3-2涉入。你针对ObjectSet书写实体查询的方式和书写一个数据库的表查询基本相同。
实体类
模型中定义的3个实体是三个实体类的来源。每个类派生自实体框架的EntityObject类,并含有基于模型中定义的属性的属性,包括Contact.Addresses和Address.Contact这些必要的导航属性(见图3-5).
图3-5 类设计器里的实体类
但是在Address类里有些新东西:ContactReferece,这是另外一种访问Contact属性的方式。你将会在第19章里了解到EntityReferece属性的细节。这些类还有很多其它成员,但是因为它们和本章里的查询没有关系,所以我们在本书后面再剖析它们。
深入挖掘吧:只管在这个生成的代码文件里闲逛,但要记住你所做的任何改动,在模型被修改和保存时都会被覆盖掉。