LINQ to Entities 执行流
针对实体框架的查询由针对对象上下文执行的命令目录树查询表示。LINQ to Entities 将语言集成查询 (LINQ) 查询转换为命令目录树查询,针对实体框架执行这些查询,并返回可同时由实体框架和 LINQ 使用的对象。以下是创建和执行 LINQ to Entities 查询的过程:
从 ObjectContext 构造 ObjectQuery 实例。
通过使用 ObjectQuery 实例在 C# 或 Visual Basic 中编写 LINQ to Entities 查询。
将 LINQ 标准查询运算符和表达式将转换为命令目录树。
对数据源执行命令目录树表示形式的查询。执行过程中在数据源上引发的任何异常都将直接向上传递到客户端。
将查询结果返回到客户端。
构造 ObjectQuery 实例
ObjectQuery 泛型类表示一个查询,此查询返回由零个或零个以上类型化实体组成的集合。对象查询通常从现有对象上下文中构造(而不是手动构造),并且始终属于该对象上下文。此上下文提供了编写和执行查询所需的连接和元数据信息。ObjectQuery 泛型类实现 IQueryable 泛型接口,借助于该接口的生成器方法,能够以增量方式生成 LINQ 查询。
编写查询
ObjectQuery 泛型类(可实现泛型 IQueryable 接口)的实例可充当 LINQ to Entities 查询的数据源。在查询中,您可以确切指定要从数据源检索哪些信息。查询也可以指定返回信息之前信息的排序、分组和表现方式。在 LINQ 中,查询存储在变量中。此查询变量不执行任何操作,也不返回任何数据;它只存储查询信息。创建查询后必须执行该查询以检索任何数据。
可以通过两种语法编写 LINQ to Entities 查询:查询表达式语法和基于方法的查询语法。查询表达式语法和基于方法的查询语法是 C# 3.0 和 Visual Basic 9.0 中的新增功能。
有关更多信息,请参见 LINQ to Entities 中的查询。
以下采用查询表达式语法的示例从 AdventureWorks 对象上下文构造 ObjectQuery 实例并使用 Select 以返回 Product 中的所有行。
Using AWEntities As New AdventureWorksEntities
Dim products As ObjectQuery(Of Product) = AWEntities.Product
Dim productNames = _
From p In products _
Select p
End Using
using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
{
ObjectQuery<Product> products = AWEntities.Product;
IQueryable<Product> productNames =
from p in products
select p;
}
以下采用基于方法的查询语法的示例从 AdventureWorks 对象上下文构造 ObjectQuery 实例并使用 Select 以返回 Product 中的所有行。
Using AWEntities As New AdventureWorksEntities
Dim productNames = AWEntities.Product.Select(Function(p) p.Name)
End Using
using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
{
ObjectQuery<Product> products = AWEntities.Product;
IQueryable<Product> productNames = products.Select(p => p);
}
查询转换
若要针对实体框架执行 LINQ to Entities 查询,必须将 LINQ 查询转换为可针对实体框架执行的命令目录树表示形式。
LINQ to Entities 查询由 LINQ 标准查询运算符(如 Select、Where 和 GroupBy)和表达式(x > 10、Contact.LastName 等等)组成。LINQ 运算符并非由类定义,而是由类中的方法定义。在 LINQ 中,表达式可包含 System.Linq.Expressions 命名空间内的类型所允许的任何内容,并且,通过扩展,可包含可在 lambda 函数中表示的任何内容。这是实体框架允许的表达式的超集,这些表达式在定义上限于数据库所允许的操作,并且受到 ObjectQuery 支持。
在实体框架中,运算符和表达式同时由单一类型层次结构表示,这些运算符和表达式随后会放入命令目录树中。实体框架使用命令目录树来执行此查询。如果 LINQ 查询无法表示为命令目录树,则当转换查询时,将引发异常。LINQ to Entities 查询的转换涉及两个子转换:标准查询运算符的转换和表达式的转换。
一些 LINQ 标准查询运算符在 LINQ to Entities 中没有有效的转换。尝试使用这些运算符将在查询转换期间导致异常。有关所支持的 LINQ to Entities 运算符的列表,请参见支持的和不支持的方法 (LINQ to Entities)。
有关使用 LINQ to Entities 中的标准查询运算符的更多信息,请参见 LINQ to Entities 查询中的标准查询运算符。
通常,LINQ to Entities 中的表达式将在服务器上求值,这样,表达式的行为不会遵循 CLR 语义。有关更多信息,请参见 LINQ to Entities 查询中的表达式。
查询执行
在用户创建 LINQ 查询之后,该查询将转换为与实体框架兼容的表示形式(采用命令目录树形式),然后针对数据源执行此查询。在查询执行时间,将在客户端或服务器上计算所有查询表达式(或查询的组成部分)的值。这包括在结果具体化或实体投影中使用的表达式。
执行查询表达式的时间可能会发生变化。每次循环访问查询变量时(而不是创建查询变量时)都将执行 LINQ 查询;这称为延迟执行。还可以强制立即执行此查询,这对于缓存查询结果很有用。以下示例使用 Select 以返回 Product 中的所有行并显示产品名称。在 foreach/For Each 循环中循环访问查询变量将导致执行此查询。
Using AWEntities As New AdventureWorksEntities
Dim products As ObjectQuery(Of Product) = AWEntities.Product
Dim productNames = _
From p In products _
Select p.Name
Console.WriteLine("Product Names:")
For Each productName In productNames
Console.WriteLine(productName)
Next
End Using
using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
{
ObjectQuery<Product> products = AWEntities.Product;
IQueryable<string> productNames =
from p in products
select p.Name;
Console.WriteLine("Product Names:");
foreach (var productName in productNames)
{
Console.WriteLine(productName);
}
}
当执行 LINQ to Entities 查询时,查询中的有些表达式可能在服务器上执行,而有些部分可能在客户端上本地执行。表达式的客户端计算发生于在服务器上执行查询之前。如果在客户端上计算表达式,则该计算的结果将替换查询中的表达式,然后在服务器上执行查询。由于是在数据源上执行查询,因此数据源配置将覆盖客户端中指定的行为。空值处理和数值精度就是这种情况的示例。在查询执行期间在服务器上引发的任何异常都将直接向上传递到客户端。有关更多信息,请参见查询执行。
具体化
具体化是将查询结果作为 CLR 类型返回到客户端的过程。在 LINQ to Entities 中,从不返回查询结果数据记录;始终存在一个后备 CLR 类型,该类型由用户或实体框架定义,或者由编译器生成(匿名类型)。所有对象具体化均由实体框架执行。由于无法在实体框架与 CLR 之间进行映射而导致的任何错误都将导致在对象具体化过程中引发异常。
查询结果通常以下面的某一种形式返回:
EDM 中零个或零个以上类型化实体对象的集合或复杂类型的投影。
EDM 支持的 CLR 类型。
内联集合。
匿名类型。
有关更多信息,请参见查询结果。