Querying the Data Service (WCF Data Services)
The WCF Data Services client library enables you to execute queries against a data service by using familiar .NET Framework programming patterns, including using language integrated query (LINQ). The client library translates a query, which is defined on the client as an instance of the DataServiceQuery<TElement> class, into an HTTP GET request message. The library receives the response message and translates it into instances of client data service classes. These classes are tracked by the DataServiceContext to which the DataServiceQuery<TElement> belongs.
The DataServiceQuery<TElement> generic class represents a query that returns a collection of zero or more entity type instances. A data service query always belongs to an existing data service context. This context maintains the service URI and metadata information that is required to compose and execute the query.
When you use the Add Service Reference dialog to add a data service to a .NET Framework-based client application, an entity container class is created that inherits from the DataServiceContext class. This class includes properties that return typed DataServiceQuery<TElement> instances. There is one property for each entity set that the data service exposes. These properties make it easier to create an instance of a typed DataServiceQuery<TElement>.
A query is executed in the following scenarios:
When results are enumerated implicitly, such as:
When a property on the DataServiceContext that represents and entity set is enumerated, such as during a
For Each(Visual Basic) loop.
When the query is assigned to a
The following query, when it is executed, returns all
Customers entities in the Northwind data service:
[!CODE [Astoria Northwind Client#GetAllCustomersSpecific](../CodeSnippet/VS_Snippets_Misc/astoria northwind client#getallcustomersspecific)]
For more information, see How to: Execute Data Service Queries.
The WCF Data Services client supports queries for late-bound objects, such as when you use the dynamic type in C#. However, for performance reasons you should always compose strongly-typed queries against the data service. The Tuple type and dynamic objects are not supported by the client.
Because the DataServiceQuery<TElement> class implements the IQueryable<T> interface defined by LINQ, the WCF Data Services client library is able to transform LINQ queries against entity set data into a URI that represents a query expression evaluated against a data service resource. The following example is a LINQ query that is equivalent to the previous DataServiceQuery<TElement> that returns
Orders that have a freight cost of more than $30 and orders the results by the freight cost:
[!CODE [Astoria NorthwindClient#AddQueryOptionsLinqSpecific](Astoria NorthwindClient#AddQueryOptionsLinqSpecific)]
This LINQ query is translated into the following query URI that is executed against the Northwind-based quickstart data service:
http://localhost:12345/Northwind.svc/Orders?Orderby=ShippedDate&?filter=Freight gt 30
The set of queries expressible in the LINQ syntax is broader than those enabled in the representational state transfer (REST)-based URI syntax that is used by data services. A NotSupportedException is raised when the query cannot be mapped to a URI in the target data service.
For more information, see LINQ Considerations.
Data service queries support all the query options that WCF Data Servicess provides. You call the AddQueryOption method to append query options to a DataServiceQuery<TElement> instance. AddQueryOption returns a new DataServiceQuery<TElement> instance that is equivalent to the original query but with the new query option set. The following query, when executed, returns
Orders that are filtered by the
Freight value and ordered by the
[!CODE [Astoria NorthwindClient#AddQueryOptionsSpecific](Astoria NorthwindClient#AddQueryOptionsSpecific)]
You can use the
$orderby query option to both order and filter a query based on a single property, as in the following example that filters and orders the returned
Orders objects based on the value of the
[!CODE [Astoria Northwind Client#OrderWithFilter](../CodeSnippet/VS_Snippets_Misc/astoria northwind client#orderwithfilter)]
Query options give you another way to express the syntactic components of a LINQ query. For more information, see LINQ Considerations.
The client executes a query in two parts. Whenever possible, expressions in a query are first evaluated on the client, and then a URI-based query is generated and sent to the data service for evaluation against data in the service. Consider the following LINQ query:
[!CODE [Astoria NorthwindClient#LinqQueryClientEvalSpecific](Astoria NorthwindClient#LinqQueryClientEvalSpecific)]
In this example, the expression
(basePrice – (basePrice * discount)) is evaluated on the client. Because of this, the actual query URI
http://localhost:12345/northwind.svc/Products()?$filter=(UnitPrice gt 90.00M) and substringof('bike',ProductName) that is sent to the data service contains the already calculated decimal value of
90 in the filter clause. The other parts of the filtering expression, including the substring expression, are evaluated by the data service. Expressions that are evaluated on the client follow common language runtime (CLR) semantics, while expressions sent to the data service rely on the data service implementation of the OData Protocol. You should also be aware of scenarios where this separate evaluation may cause unexpected results, such as when both the client and service perform time-based evaluations in different time zones.
[!CODE [Astoria Northwind Client#GetResponseSpecific](../CodeSnippet/VS_Snippets_Misc/astoria northwind client#getresponsespecific)]
The entity type instances that represent entities in the data service are created on the client by a process called object materialization. For more information, see Object Materialization. The QueryOperationResponse<T> object implements IEnumerable<T> to provide access to the results of the query.
The QueryOperationResponse<T> also has the following members that enable you to access additional information about a query result:
Error - gets an error thrown by the operation, if any has occurred.
Headers - contains the collection of HTTP response headers associated with the query response.
StatusCode - gets the HTTP response code for the query response.
By default, WCF Data Services only returns data that is explicitly selected by the query URI. This gives you the option to explicitly load additional data from the data service when it is needed. A request is sent to the data service each time you explicitly load data from the data service. Data that can be explicitly loaded includes related entities, paged response data, and binary data streams.
Because a data service may return a paged response, we recommend that your application use the programming pattern to handle a paged data service response. For more information, see Loading Deferred Content.
The amount of data returned by a query can also be reduced by specifying that only certain properties of an entity are returned in the response. For more information, see Query Projections.
In some scenarios, it is helpful to know the total number of entities in an entity set and not merely the number returned by the query. Call the IncludeTotalCount method on the DataServiceQuery<TElement> to request that this total count of entities in the set be included with the query result. In this case, the TotalCount property of the returned QueryOperationResponse<T> returns the total number of entities in the set.
You can also get only the total count of entities in the set either as an Int32 or as a Int64 value by calling the Count<TSource> or LongCount<TSource> methods respectively. When these methods are called, a QueryOperationResponse<T> is not returned; only the count value is returned. For more information, see How to: Determine the Number of Entities Returned by a Query.