LINQ 注意事项(WCF 数据服务)
本主题提供有关使用 WCF 数据服务 客户端时 LINQ 查询的编写和执行方式的信息以及使用 LINQ 查询实现 开放式数据协议 (OData) 的数据服务的限制。有关编写和执行针对基于 OData 的数据服务的查询的更多信息,请参见查询数据服务(WCF 数据服务)。
编写 LINQ 查询
LINQ 允许针对实现 IEnumerable 的对象集合编写查询。可以使用 Visual Studio 中的**“添加服务引用”**对话框和 DataSvcUtil.exe 工具生成 OData 服务的表示形式以作为继承自 DataServiceContext 的实体容器类,以及代表在源中返回的实体的对象。这些工具还可为由服务作为源公开的集合的实体容器类生成属性。封装了数据服务的类的每个属性都会返回一个 DataServiceQuery。由于 DataServiceQuery 类实现 LINQ 定义的 IQueryable 接口,因此 WCF 数据服务 可以针对数据服务公开的源编写 LINQ 查询,客户端库会将其转换为一个查询请求 URI 并发送给所执行的数据服务。
注意: |
---|
可以采用 LINQ 语法表示的查询集大于 OData 数据服务使用的 URI 语法所支持的查询集。如果无法将查询映射到目标数据服务中的 URI,则会引发 NotSupportedException。有关更多信息,请参见本主题中的Unsupported LINQ Methods。 |
下面的示例是一个 LINQ 查询,它返回运费成本超过 30 美元的 Orders
并按装运日期对结果进行排序,最近的装运日期排在最前面:
Dim selectedOrders = From o In context.Orders _
Where (o.Freight > 30) _
Order By o.ShippedDate Descending _
Select o
var selectedOrders = from o in context.Orders
where o.Freight > 30
orderby o.ShippedDate descending
select o;
此 LINQ 查询转换为以下查询 URI,它针对基于罗斯文的快速入门数据服务来执行:
https://localhost:12345/Northwind.svc/Orders?Orderby=ShippedDate&?filter=Freight gt 30
有关 LINQ 的更多信息,请参见Language-Integrated Query (LINQ)。
LINQ 允许使用语言特定声明查询语法(如上例所示)和一组称为标准查询运算符的查询方法编写查询。通过仅使用基于方法的语法可以编写与上例等效的查询,如下面的示例所示:
Dim selectedOrders = context.Orders _
.Where(Function(o) o.Freight.Value > 30) _
.OrderByDescending(Function(o) o.ShippedDate)
var selectedOrders = context.Orders
.Where(o => o.Freight > 30)
.OrderByDescending(o => o.ShippedDate);
WCF 数据服务 客户端能够将用这两种方式编写的查询转换为查询 URI,并且可以通过向查询表达式追加查询方法来扩展 LINQ 查询。通过向查询表达式或 DataServiceQuery 追加方法语法来编写 LINQ 查询时,运算将按照方法的调用顺序添加到查询 URI 中。这等效于调用 AddQueryOption 方法将每个查询选项添加到查询 URI 中。
执行 LINQ 查询
某些 LINQ 查询方法(如 First 或 Single)在追加到查询中时会导致执行查询。在隐式枚举结果时也会执行查询,例如在 foreach 循环过程中或将查询分配给一个 List 集合时。有关更多信息,请参见查询数据服务(WCF 数据服务)。
客户端分两个部分来执行 LINQ 查询。只要有可能,将首先在客户端上计算查询中的 LINQ 表达式,然后生成基于 URI 的查询并将其发送至数据服务,以针对服务中的数据进行计算。有关更多信息,请参见查询数据服务(WCF 数据服务)中的Client versus Server Execution部分。
如果 LINQ 查询无法在 OData 兼容的查询 URI 中进行转换,则尝试执行时将引发异常。有关更多信息,请参见查询数据服务(WCF 数据服务)。
LINQ 查询示例
以下各小节中的示例揭示了可针对 OData 服务执行的 LINQ 查询的种类。
筛选
本小节中的 LINQ 查询示例将筛选由服务返回的源中的数据。
下面这些示例是等效的查询,它们将筛选返回的 Orders
实体并仅返回运费成本高于 30 美元的订单:
使用 LINQ 查询语法:
Dim filteredOrders = From o In context.Orders Where o.Freight.Value > 30 Select o
var filteredOrders = from o in context.Orders where o.Freight > 30 select o;
使用 LINQ 查询方法:
Dim filteredOrders = context.Orders.Where(Function(o) o.Freight.Value > 0)
var filteredOrders = context.Orders .Where(o => o.Freight > 30);
URI 查询字符串 $filter 选项:
' Define a query for orders with a Freight value greater than 30. Dim filteredOrders _ = context.Orders.AddQueryOption("$filter", "Freight gt 30M")
// Define a query for orders with a Freight value greater than 30. var filteredOrders = context.Orders.AddQueryOption("$filter", "Freight gt 30M");
前面的所有示例都将转换为查询 URI:https://localhost:12345/northwind.svc/Orders()?$filter=Freight gt 30M
。
排序
下面的示例显示了按公司名称和邮政编码降序排序返回的数据的等效查询:
使用 LINQ 查询语法:
Dim sortedCustomers = From c In context.Customers Order By c.CompanyName Ascending, c.PostalCode Descending Select c
var sortedCustomers = from c in context.Customers orderby c.CompanyName ascending, c.PostalCode descending select c;
使用 LINQ 查询方法:
Dim sortedCustomers = context.Customers.OrderBy(Function(c) c.CompanyName) _ .ThenByDescending(Function(c) c.PostalCode)
var sortedCustomers = context.Customers.OrderBy(c => c.CompanyName) .ThenByDescending(c => c.PostalCode);
URI 查询字符串 $orderby 选项:
Dim sortedCustomers = context.Customers _ .AddQueryOption("$orderby", "CompanyName, PostalCode desc")
var sortedCustomers = context.Customers .AddQueryOption("$orderby", "CompanyName, PostalCode desc");
前面的所有示例都将转换为查询 URI:https://localhost:12345/northwind.svc/Customers()?$orderby=CompanyName,PostalCode desc
。
投影
下面的示例显示了将返回的数据投影到较窄的 CustomerAddress
类型的等效查询:
使用 LINQ 查询语法:
Dim projectedQuery = From c In context.Customers Select New CustomerAddress With { .CustomerID = c.CustomerID, .Address = c.Address, .City = c.City, .Region = c.Region, .PostalCode = c.PostalCode, .Country = c.Country }
var projectedQuery = from c in context.Customers select new CustomerAddress { CustomerID = c.CustomerID, Address = c.Address, City = c.City, Region = c.Region, PostalCode = c.PostalCode, Country = c.Country };
使用 LINQ 查询方法:
Dim projectedQuery = context.Customers.Where(Function(c) c.Country = "Germany") _ .Select(Function(c) New CustomerAddress With { .CustomerID = c.CustomerID, .Address = c.Address, .City = c.City, .Region = c.Region, .PostalCode = c.PostalCode, .Country = c.Country })
var projectedQuery = context.Customers.Where(c => c.Country == "Germany") .Select(c => new CustomerAddress { CustomerID = c.CustomerID, Address = c.Address, City = c.City, Region = c.Region, PostalCode = c.PostalCode, Country = c.Country});
注意: |
---|
不能使用 AddQueryOption 方法将 $select 查询选项添加到查询 URI 中。我们建议使用 LINQ Select 方法,让客户端在请求 URI 中生成 $select 查询选项。 |
前面的两个示例都将转换为查询 URI:"https://localhost:12345/northwind.svc/Customers()?$filter=Country eq 'GerGerm'&$select=CustomerID,Address,City,Region,PostalCode,Country"
。
客户端分页
下面所示的等效查询示例将请求返回一页包含 25 个订单的排序订单实体,并跳过前 50 个订单:
为 LINQ 查询应用查询方法:
Dim pagedOrders = (From o In context.Orders Order By o.OrderDate Descending Select o) _ .Skip(50).Take(25)
var pagedOrders = (from o in context.Orders orderby o.OrderDate descending select o).Skip(50).Take(25);
URI 查询字符串 $skip 和 $top 选项:
Dim pagedOrders = context.Orders _ .AddQueryOption("$orderby", "OrderDate desc") _ .AddQueryOption("$skip", 50) _ .AddQueryOption("$top", 25) _
var pagedOrders = context.Orders .AddQueryOption("$orderby", "OrderDate desc") .AddQueryOption("$skip", 50) .AddQueryOption("$top", 25);
前面的两个示例都将转换为查询 URI:https://localhost:12345/northwind.svc/Orders()?$orderby=OrderDate desc&$skip=50&$top=25
。
展开
查询 OData 数据服务时,可以请求在返回的源中包含与查询的目标实体相关的实体。针对 LINQ 查询的目标实体集调用 DataServiceQuery 的 Expand 方法,相关实体集名称作为 path 参数提供。有关更多信息,请参见加载延迟的内容(WCF 数据服务)。
下面的示例显示了在查询中使用 Expand 方法的等效方式:
在 LINQ 查询语法中:
Dim ordersQuery = From o In context.Orders.Expand("Order_Details") Where o.CustomerID = "ALFKI" Select o
var ordersQuery = from o in context.Orders.Expand("Order_Details") where o.CustomerID == "ALFKI" select o;
使用 LINQ 查询方法:
Dim ordersQuery = context.Orders.Expand("Order_Details") _ .Where(Function(o) o.CustomerID = "ALFKI")
var ordersQuery = context.Orders.Expand("Order_Details") .Where(o => o.CustomerID == "ALFKI");
前面的两个示例都将转换为查询 URI:https://localhost:12345/northwind.svc/Orders()?$filter=CustomerID eq 'ALFKI'&$expand=Order_Details
。
不支持的 LINQ 方法
下表所列的 LINQ 方法的类不受支持,不能包含在针对 OData 服务执行的查询中:
运算类型 | 不支持的方法 |
---|---|
集合运算符 |
所有集合运算符都不能用于 DataServiceQuery,其中包括: |
排序运算符 |
要求 IComparer 的以下排序运算符不能用于 DataServiceQuery: |
投影和筛选运算符 |
以下接受位置参数的投影和筛选运算符不能用于 DataServiceQuery: |
分组运算符 |
所有分组运算符都不能用于 DataServiceQuery,其中包括: 分组运算符必须在客户端上执行。 |
聚合运算符 |
所有聚合运算符都不能用于 DataServiceQuery,其中包括: 聚合运算符必须在客户端上执行或由服务操作封装。 |
分页运算符 |
以下分页运算符不能用于 DataServiceQuery:
注意:
针对空序列执行的分页运算符将返回 null。
|
其他运算符 |
以下其他运算符不能用于 DataServiceQuery: |
支持的表达式函数
支持以下公共语言运行时 (CLR) 方法和属性,因为它们可以在查询表达式中进行转换以包含在 OData 服务的请求 URI 中:
String 成员 | 支持的 OData 函数 |
---|---|
string concat(string p0, string p1) |
|
bool substringof(string p0, string p1) |
|
bool endswith(string p0, string p1) |
|
int indexof(string p0, string p1) |
|
int length(string p0) |
|
string replace(string p0, string find, string replace) |
|
string substring(string p0, int pos) |
|
string substring(string p0, int pos, int length) |
|
string tolower(string p0) |
|
string toupper(string p0) |
|
string trim(string p0) |
DateTime 成员1 | 支持的 OData 函数 |
---|---|
int day(DateTime p0) |
|
int hour(DateTime p0) |
|
int minute(DateTime p0) |
|
int month(DateTime p0) |
|
int second(DateTime p0) |
|
int year(DateTime p0) |
1也支持 Visual Basic 中等效的 Microsoft.VisualBasic.DateAndTime 的日期和时间属性以及 DatePart 方法。
Math 成员 | 支持的 OData 函数 |
---|---|
decimal ceiling(decimal p0) |
|
double ceiling(double p0) |
|
decimal floor(decimal p0) |
|
double floor(double p0) |
|
decimal round(decimal p0) |
|
double round(double p0) |
Expression 成员 | 支持的 OData 函数 |
---|---|
bool isof(type p0) |
客户端或许还可以在客户端上计算其他 CLR 函数。对于无法在客户端上计算以及无法转换为有效请求 URI 以便在服务器上计算的任何表达式,将引发 NotSupportedException。
另请参见
概念
查询数据服务(WCF 数据服务)
查询投影(WCF 数据服务)
对象具体化(WCF 数据服务)