Share via


LINQ 注意事项(WCF 数据服务)

本主题提供有关使用 WCF 数据服务 客户端时 LINQ 查询的编写和执行方式的信息以及使用 LINQ 查询实现开放式数据协议 (OData) 的数据服务的限制。 有关以下内容的更多信息编写和执行针对基于 OData 数据服务的查询的更多信息,请参见查询数据服务(WCF 数据服务)

本主题包含以下各节:

编写 LINQ 查询

LINQ 允许针对实现 IEnumerable<T> 的对象集合编写查询。 可以使用 Visual Studio 中的**“添加服务引用”**对话框和 DataSvcUtil.exe 工具生成 OData 服务的表示形式以作为继承自 DataServiceContext 的实体容器类,以及代表在源中返回的实体的对象。 这些工具还可为由服务作为源公开的集合的实体容器类生成属性。 封装了数据服务的类的每个属性都会返回一个 DataServiceQuery<TElement>。 因为 DataServiceQuery<TElement> 类实现 LINQ 定义的 IQueryable<T> 接口,所以可以针对数据服务公开的源编写 LINQ 查询,客户端库会将其转换为一个查询请求 URI,在执行时将此 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<TElement> 追加方法语法来编写 LINQ 查询时,运算将按照方法的调用顺序添加到查询 URI 中。 这等效于调用 AddQueryOption(String, Object) 方法将每个查询选项添加到查询 URI 中。

执行 LINQ 查询

某些 LINQ 查询方法(如 First<TSource>Single<TSource>)在追加到查询中时会导致执行查询。 在隐式枚举结果时也会执行查询,例如在 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。

还可以使用 All<TSource>(IEnumerable<TSource>, Func<TSource, Boolean>)Any<TSource>(IEnumerable<TSource>) 运算符编写根据集合属性筛选实体的查询。 这种情况下,将对返回相关实体的集合与基元和复杂类型的集合的属性计算谓词。 例如,下面的查询返回在员工的地区介绍中包含所提供的字符串的所有员工:

Dim filteredEmployees = From e In context.Employees _
                        Where e.Territories.Any(Function(t) t.TerritoryDescription.Contains(territory))
                        Select e
var filteredEmployees = from e in context.Employees
                        where e.Territories.Any(t => t.TerritoryDescription.Contains(territory))
                        select e;

在此查询中,Any<TSource>(IEnumerable<TSource>) 运算符用来遍历 Employees 与 Territories 之间的多对多关联,以根据相关地区的计算筛选员工。 有关更多信息,请参见文章 Any/All support in WCF Data Services(WCF Data Services 中的 Any/All 支持)。

排序

下面的示例显示了按公司名称和邮政编码降序排序返回的数据的等效查询:

  • 使用 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(String, Object) 方法将 $select 查询选项添加到查询 URI 中。我们建议使用 LINQ Select<TSource, TResult>(IEnumerable<TSource>, Func<TSource, TResult>) 方法,让客户端在请求 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<TElement>Expand(String) 方法,相关实体集名称作为 path 参数提供。 有关更多信息,请参见加载延迟的内容(WCF 数据服务)

下面的示例显示了在查询中使用 Expand(String) 方法的等效方式:

  • 在 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<TElement>

排序运算符

要求 IComparer<T> 的以下排序运算符不能用于 DataServiceQuery<TElement>

投影和筛选运算符

以下接受位置参数的投影和筛选运算符不能用于 DataServiceQuery<TElement>

分组运算符

所有分组运算符都不能用于 DataServiceQuery<TElement>,其中包括:

分组运算符必须在客户端上执行。

聚合运算符

所有聚合运算符都不能用于 DataServiceQuery<TElement>,其中包括:

聚合运算符必须在客户端上执行或由服务操作封装。

分页运算符

以下分页运算符不能用于 DataServiceQuery<TElement>

备注

针对空序列执行的分页运算符将返回 null。

其他运算符

以下其他运算符不能用于 DataServiceQuery<TElement>

  1. Empty<TResult>

  2. Range

  3. Repeat<TResult>

  4. ToDictionary

  5. ToLookup

支持的表达式函数

支持以下公共语言运行时 (CLR) 方法和属性,因为它们可以在查询表达式中进行转换以包含在 OData 服务的请求 URI 中:

String 成员

支持的 OData 函数

Concat

string concat(string p0, string p1)

Contains

bool substringof(string p0, string p1)

EndsWith

bool endswith(string p0, string p1)

IndexOf

int indexof(string p0, string p1)

Length

int length(string p0)

Replace

string replace(string p0, string find, string replace)

Substring

string substring(string p0, int pos)

Substring

string substring(string p0, int pos, int length)

ToLower

string tolower(string p0)

ToUpper

string toupper(string p0)

Trim

string trim(string p0)

DateTime 成员1

支持的 OData 函数

Day

int day(DateTime p0)

Hour

int hour(DateTime p0)

Minute

int minute(DateTime p0)

Month

int month(DateTime p0)

Second

int second(DateTime p0)

Year

int year(DateTime p0)

1也支持 Visual Basic 中等效的 Microsoft.VisualBasic.DateAndTime 的日期和时间属性以及 DatePart 方法。

Math 成员

支持的 OData 函数

Ceiling

decimal ceiling(decimal p0)

Ceiling

double ceiling(double p0)

Floor

decimal floor(decimal p0)

Floor

double floor(double p0)

Round

decimal round(decimal p0)

Round

double round(double p0)

Expression 成员

支持的 OData 函数

TypeIs

bool isof(type p0)

客户端或许还可以在客户端上计算其他 CLR 函数。 对于无法在客户端上计算以及无法转换为有效请求 URI 以便在服务器上计算的任何表达式,将引发 NotSupportedException

版本控制要求

LINQ 支持具有以下 OData 协议版本控制要求:

有关更多信息,请参见数据服务版本管理(WCF 数据服务)

请参阅

概念

查询数据服务(WCF 数据服务)

查询投影(WCF 数据服务)

对象具体化(WCF 数据服务)

其他资源

OData:URI 约定