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

利用 WCF 数据服务客户端库,可以使用熟悉的 .NET Framework 编程模式针对数据服务执行查询,包括使用语言集成查询 (LINQ)。 客户端库将在客户端上定义为 DataServiceQuery<TElement> 类实例的查询转换为 HTTP GET 请求消息。 该库接收响应消息并将该消息转换为客户端数据服务类的实例。 DataServiceQuery<TElement> 所属的 DataServiceContext 跟踪这些类。

数据服务查询

DataServiceQuery<TElement> 泛型类表示一个查询,该查询返回一个包含零个或零个以上实体类型实例的集合。 数据服务查询始终属于现有数据服务上下文。 此上下文含有撰写和执行查询所必需的服务 URI 和元数据信息。

使用**“添加服务引用”**对话框向基于 .NET Framework 的客户端应用程序添加数据服务时,将创建一个继承自 DataServiceContext 类的实体容器类。 此类包括返回类型化 DataServiceQuery<TElement> 实例的属性。 数据服务公开的每个实体集对应一个属性。 使用这些属性可以更容易地创建类型化 DataServiceQuery<TElement> 的实例。

在以下情况下会执行查询:

  • 隐式枚举结果时,如:

    • DataServiceContext 上的某个属性表示并枚举实体集时,如在 foreach (C#) 或 For Each (Visual Basic) 循环期间。

    • 将查询赋给 List 集合时。

  • 显式调用 ExecuteBeginExecute 方法时。

  • 调用 LINQ 查询执行运算符(例如 FirstSingle)时。

下面的查询在执行时返回罗斯文数据服务中的所有 Customers 实体:

' Define a new query for Customers.
Dim query As DataServiceQuery(Of Customer) = context.Customers
// Define a new query for Customers.
DataServiceQuery<Customer> query = context.Customers;

有关更多信息,请参见如何:执行数据服务查询(WCF 数据服务)

WCF 数据服务客户端支持后期绑定对象的查询,例如在 C# 中使用动态类型时。 但是,出于性能原因,应始终编写针对数据服务的强类型查询。 客户端不支持 Tuple 类型和动态对象。

LINQ 查询

由于 DataServiceQuery<TElement> 类实现 LINQ 定义的 IQueryable<T> 接口,因此 WCF 数据服务客户端库能够将针对实体集数据的 LINQ 查询转换为一个 URI,该 URI 表示针对数据服务资源计算的查询表达式。 下面的示例是一个等效于之前 DataServiceQuery<TElement> 的 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 语法表示的查询集大于数据服务使用的基于具象状态传输 (REST) 的 URI 语法所支持的查询集。如果无法将查询映射到目标数据服务中的 URI,则会引发 NotSupportedException

有关更多信息,请参见 LINQ 注意事项(WCF 数据服务)

添加查询选项

数据服务查询支持 WCF 数据服务提供的所有查询选项。 调用 AddQueryOption 方法可向 DataServiceQuery<TElement> 实例追加查询选项。 AddQueryOption 返回一个新的 DataServiceQuery<TElement> 实例,该实例等效于原始查询,但带有新的查询选项集。 下面的查询在执行时会返回按 Freight 值进行筛选并按 OrderID 降序排序的 Orders:

' Define a query for orders with a Freight value greater than 30
' and that is ordered by the ship date, descending.
Dim selectedOrders As DataServiceQuery(Of Order) = context.Orders _
.AddQueryOption("$filter", "Freight gt 30") _
.AddQueryOption("$orderby", "OrderID desc")
// Define a query for orders with a Freight value greater than 30
// and that is ordered by the ship date, descending.
DataServiceQuery<Order> selectedOrders = context.Orders
    .AddQueryOption("$filter", "Freight gt 30")
    .AddQueryOption("$orderby", "OrderID desc");

可以使用 $orderby 查询选项基于单个属性对查询进行排序和筛选,如下面的示例所示,该示例基于 Freight 属性的值对返回的 Orders 对象进行筛选和排序:

' Create the DataServiceContext using the service URI.
Dim context = New NorthwindEntities(svcUri)

' Define a query for orders with a Freight value greater than 30
' that also orders the result by the Freight value, descending.
Dim selectedOrders As DataServiceQuery(Of Order) = _
context.Orders.AddQueryOption("$orderby", "Freight gt 30 desc")

Try
    ' Enumerate over the results of the query.
    For Each order As Order In selectedOrders
        Console.WriteLine("Order ID: {0} - Freight: {1}", _
                order.OrderID, order.Freight)
    Next
Catch ex As DataServiceQueryException
    Throw New ApplicationException( _
            "An error occurred during query execution.", ex)
End Try
// Create the DataServiceContext using the service URI.
NorthwindEntities context = new NorthwindEntities(svcUri);

// Define a query for orders with a Freight value greater than 30
// that also orders the result by the Freight value, descending.
DataServiceQuery<Order> selectedOrders = context.Orders
    .AddQueryOption("$orderby", "Freight gt 30 desc");

try
{
    // Enumerate over the results of the query.
    foreach (Order order in selectedOrders)
    {
        Console.WriteLine("Order ID: {0} - Freight: {1}",
            order.OrderID, order.Freight);
    }
}
catch (DataServiceQueryException ex)
{
    throw new ApplicationException(
        "An error occurred during query execution.", ex);
}

可以连续调用 AddQueryOption 方法来构造复杂的查询表达式。 有关更多信息,请参见如何:向数据服务查询添加查询选项(WCF 数据服务)

查询选项为表示 LINQ 查询的语法成分提供了另一种方法。 有关更多信息,请参见 LINQ 注意事项(WCF 数据服务)

备注

不能使用 AddQueryOption(String, Object) 方法将 $select 查询选项添加到查询 URI 中。我们建议使用 LINQ Select<TSource, TResult>(IEnumerable<TSource>, Func<TSource, TResult>) 方法,让客户端在请求 URI 中生成 $select 查询选项。

客户端执行与服务器执行

客户端分两个部分来执行查询。 只要有可能,将首先在客户端上计算查询中的表达式,然后生成基于 URI 的查询并将其发送至数据服务,以针对服务中的数据进行计算。 请考虑以下 LINQ 查询:

Dim basePrice As Integer = 100
Dim discount As Decimal = Convert.ToDecimal(0.1)

' Define a query that returns products based on a 
' calculation that is determined on the client.
Dim productsQuery = From p In context.Products
                  Where p.UnitPrice >
                  (basePrice - (basePrice * discount)) AndAlso
                  p.ProductName.Contains("bike")
                  Select p
int basePrice = 100;
decimal discount = .10M;

// Define a query that returns products based on a 
// calculation that is determined on the client.
var productsQuery = from p in context.Products
                  where p.UnitPrice >
                  (basePrice - (basePrice * discount)) &&
                  p.ProductName.Contains("bike")
                  select p;

在本例中,表达式 (basePrice – (basePrice * discount)) 在客户端上进行计算。 因此,发送至数据服务的实际查询 URI https://localhost:12345/northwind.svc/Products()?$filter=(UnitPrice gt 90.00M) and substringof('bike',ProductName) 将在筛选子句中包含已计算的十进制值 90。 数据服务将计算筛选表达式的其他部分,包括子字符串表达式。 在客户端上计算的表达式遵循公共语言运行时 (CLR) 语义,而发送至数据服务的表达式则依赖于 OData 协议的数据服务实现。 还应当注意这种分别计算会导致意外结果的情况,例如当客户端和服务同时在不同时区中执行基于时间的计算时。

查询响应

执行 DataServiceQuery<TElement> 时将返回所请求的实体类型的 IEnumerable<T>。 可以将此查询结果强制转换为 QueryOperationResponse<T> 对象,如下面的示例所示:

' Execute the query for all customers and get the response object.
Dim response As QueryOperationResponse(Of Customer) = _
    CType(query.Execute(), QueryOperationResponse(Of Customer))
// Execute the query for all customers and get the response object.
QueryOperationResponse<Customer> response = 
    query.Execute() as QueryOperationResponse<Customer>;

表示数据服务中的实体的实体类型实例是在客户端上由称为对象具体化的过程创建的。 有关更多信息,请参见对象具体化(WCF 数据服务)QueryOperationResponse<T> 对象实现 IEnumerable<T> 以提供对查询结果的访问。

QueryOperationResponse<T> 还包括下列成员,它们可用来访问有关查询结果的其他信息:

默认情况下,WCF 数据服务 仅返回由查询 URI 显式选择的数据。 这样即提供了在需要时从数据服务显式加载其他数据的选项。 每次从数据服务显式加载数据时都会向数据服务发送一个请求。 可以显式加载的数据包括相关实体、分页响应数据以及二进制数据流。

备注

由于数据服务可能返回分页响应,因此建议您的应用程序使用编程模式来处理分页的数据服务响应。有关更多信息,请参见加载延迟的内容(WCF 数据服务)

还可以通过指定在响应中仅返回某个实体的某些属性来减少查询返回的数据量。 有关更多信息,请参见查询投影(WCF 数据服务)

获取集合中实体的总数

在某些方案中,不仅知道查询返回的实体数,还要知道实体集中实体的总数,这一点非常有帮助。 对 DataServiceQuery<TElement> 调用 IncludeTotalCount 方法可请求在查询结果中包含集合中实体的总数。 在这种情况下,返回的 QueryOperationResponse<T>TotalCount 属性返回集合中实体的总数。

还可以仅获取集合中实体的总数,该总数可作为 Int32Int64 值,方法是分别调用 CountLongCount 方法。 调用这些方法时,不会返回 QueryOperationResponse<T>;仅返回计数值。 有关更多信息,请参见如何:确定查询返回的实体数量(WCF 数据服务)

本节内容

查询投影(WCF 数据服务)

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

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

如何:执行数据服务查询(WCF 数据服务)

如何:向数据服务查询添加查询选项(WCF 数据服务)

如何:确定查询返回的实体数量(WCF 数据服务)

如何:为数据服务请求(WCF 数据服务)指定客户端凭据

如何:设置客户端请求(WCF 数据服务)中的标头

如何:投影查询结果(WCF 数据服务)

请参阅

其他资源

数据客户端 (WCF Data Services)