Querying the Data Service (ADO.NET Data Services)

Note Note

This topic describes new functionality in ADO.NET Data Services that is available as an update to the .NET Framework version 3.5 Service Pack 1. You can download and install the update from the Microsoft Download Center.

The ADO.NET Data Services client library enables you to execute queries against a data service by using familiar .NET Framework programming, including using language integrated query (LINQ). The client library translates a query, which is defined on the client as an instance of the DataServiceQuery 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 belongs.

The DataServiceQuery 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 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.

A query is executed in the following scenarios:

  • When results are enumerated implicitly, such as in the following scenarios:

    • When a property on the DataServiceContext that represents and entity set is enumerated, such as during a foreach (C#) or For Each (Visual Basic) loop.

    • When the query is assigned to a List collection.

  • When the Execute() or BeginExecute(AsyncCallback, Object) method is explicitly called.

  • When a LINQ query execution operator, such as First``1(IEnumerable<UMP>) or Single``1(IEnumerable<UMP>) is called.

The following query, when it is executed, returns all Customers entities in the Northwind data service:

// Define a new query for Customers.
DataServiceQuery<Customers> query = context.Customers;

For more information, see How to: Execute Data Service Queries (ADO.NET Data Services).

Because the DataServiceQuery class implements the IQueryable interface defined by LINQ, the ADO.NET 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 that returns Orders that have a freight cost of more than $30 and orders the results by the freight cost:

// Define a query for orders with a Freight value greater than 30
// and that is ordered by the ship date, descending.
var selectedOrders = from o in context.Orders
                     where o.Freight > 30
                     orderby o.ShippedDate descending 
                     select o;

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.

Data service queries support all the query options that ADO.NET Data Services provides. You call the AddQueryOption(String, Object) method to append query options to a DataServiceQuery instance. AddQueryOption(String, Object) returns a new DataServiceQuery 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 OrderID, descending:

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

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 Freight property:

// 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<Orders> selectedOrders = context.Orders
    .AddQueryOption("$orderby", "Freight gt 30 desc");

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

You can call the AddQueryOption(String, Object) method consecutively to construct complex query expressions. For more information, see How to: Add Query Options to a Data Service Query (ADO.NET Data Services).

Query options give you another way to express the syntactic components of a LINQ query. For more information, see LINQ Query Syntax versus Method Syntax (C#).

When executed, the DataServiceQuery returns an IEnumerable of the requested entity type. This query result can be cast to a QueryOperationResponse object, as in the following example:

// Execute the query for all customers and get the response object.
QueryOperationResponse<Customers> response = 
    query.Execute() as QueryOperationResponse<Customers>;

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 (ADO.NET Data Services). The QueryOperationResponse object implements IEnumerable to provide access to the results of the query. The QueryOperationResponse 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.

  • Query() - gets the original DataServiceQuery that generated the QueryOperationResponse.

  • StatusCode() - gets the HTTP response code for the query response.

  • TotalCount() - gets the total number of entities in the entity set when the IncludeTotalCount() method was called on the DataServiceQuery.

  • GetContinuation() - returns a DataServiceQueryContinuation object that contains the URI of the next page of results.

By default, ADO.NET 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.

Note Note

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 (ADO.NET Data Services).

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 (ADO.NET Data Services).

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 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 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``1(IEnumerable<UMP>) or LongCount``1(IEnumerable<UMP>) methods respectively. When these methods are called, a QueryOperationResponse is not returned; only the count value is returned. For more information, see How to: Determine the Number of Entities Returned by a Query (ADO.NET Data Services).