Executing Service Operations

Description and code samples
Table of Contents
Description

When generating the data service client classes from the metadata the FunctionImport elements are ignored and therefore no method exists on the generated data service context that can be used to call a (custom) service operation directly.

To execute a custom GET service operation:

  • Call the Execute method on the DataServiceContext object, supplying the URI of the service operation, along with any parameters;
  • Use the CreateQuery<T> method on the DataServiceContext object to create a DataServiceQuery<T> object by suppling the name of the service operation.

To execute a custom POST service operation:

  • Use an HttpWebRequest object since the WCF Data Services client library does not support calling POST service operations
Note

When calling Execute method to call a service operation, the escaping of any user-supplied string values must be performed manually.

Single-quotes in URIs are escaped as pairs of single-quotes.

Operations
Return a single entity using Execute<T>
How to do it
// Define the query URI to access the service operation,
// relative to the service URI.
string queryString = "GetNewestOrder";

// Create the DataServiceContext using the service URI.
TrainingEntities context = new TrainingEntities(new Uri("http://localhost/TrainingService1/"));

try
{
    // Execute a service operation that returns only the newest single order.
    Order order
        = (context.Execute<Order>(new Uri(queryString, UriKind.Relative)))
        .FirstOrDefault();

    // Write out order information.
    Console.WriteLine(string.Format("Order ID: {0}", order.OrderID));
    Console.WriteLine(string.Format("Order date: {0}", order.OrderDate));
}
catch (DataServiceQueryException ex)
{
    QueryOperationResponse response = ex.Response;

    Console.WriteLine(response.Error.Message);
}
Return a collection of entities using Execute<T>
How to do it
// Define the service operation query parameter.
string city = "Lisboa";

// Define the query URI to access the service operation with specific
// query options relative to the service URI.
string queryString = string.Format("GetOrdersByCity?city='{0}'", city)
    + "&$orderby=ShippedDate desc"
    + "&$expand=Order_Details";

// Create the DataServiceContext using the service URI.
TrainingEntities context = new TrainingEntities(new Uri("http://localhost/TrainingService1/"));

try
{
    // Execute the service operation that returns all orders for the specified city.
    var results = context.Execute<Order>(new Uri(queryString, UriKind.Relative));

    // Write out order information.
    foreach (Order o in results)
    {
        Console.WriteLine(string.Format("Order ID: {0}", o.OrderID));

        foreach (Order_Detail item in o.Order_Details)
        {
            Console.WriteLine(String.Format("\tItem: {0}, quantity: {1}",
                item.ProductID, item.Quantity));
        }
    }
}
catch (DataServiceQueryException ex)
{
    QueryOperationResponse response = ex.Response;

    Console.WriteLine(response.Error.Message);
}
Return a collection of entities using CreateQuery<T>
How to do it
// Define the service operation query parameter.
string city = "Lisboa";

// Create the DataServiceContext using the service URI.
TrainingEntities context = new TrainingEntities(new Uri("http://localhost/TrainingService1/"));

// Use the CreateQuery method to create a query that accesses
// the service operation passing a single parameter.
var query = context.CreateQuery<Order>("GetOrdersByCity")
    .AddQueryOption("city", string.Format("'{0}'", city))
    .Expand("Order_Details");

try
{
    // The query is executed during enumeration.
    foreach (Order o in query)
    {
        Console.WriteLine(string.Format("Order ID: {0}", o.OrderID));

        foreach (Order_Detail item in o.Order_Details)
        {
            Console.WriteLine(String.Format("\tItem: {0}, quantity: {1}",
                item.ProductID, item.Quantity));
        }
    }
}
catch (DataServiceQueryException ex)
{
    QueryOperationResponse response = ex.Response;

    Console.WriteLine(response.Error.Message);
}
Return a single primitive value using Execute<T>
How to do it
// Define the query URI to access the service operation,
// relative to the service URI.
string queryString = "CountOpenOrders";

// Create the DataServiceContext using the service URI.
TrainingEntities context = new TrainingEntities(new Uri("http://localhost/TrainingService1/"));

try
{
    // Execute a service operation that returns the integer
    // count of open orders.
    int numOrders
        = (context.Execute<int>(new Uri(queryString, UriKind.Relative)))
        .FirstOrDefault();

    // Write out the number of open orders.
    Console.WriteLine(string.Format("Open orders as of {0}: {1}",
        DateTime.Today.Date, numOrders));
}
catch (DataServiceQueryException ex)
{
    QueryOperationResponse response = ex.Response;

    Console.WriteLine(response.Error.Message);
}
Return a collection of primitive values using Execute<T>
How to do it
// Define the query URI to access the service operation,
// relative to the service URI.
string queryString = "GetCustomerNames";

// Create the DataServiceContext using the service URI.
TrainingEntities context = new TrainingEntities(new Uri("http://localhost/TrainingService1/"));

try
{
    // Execute a service operation that returns a collection of string values.
    IEnumerable<string> customerNames
        = context.Execute<string>(new Uri(queryString, UriKind.Relative));

    foreach (string name in customerNames)
    {
        // Write out order information.
        Console.WriteLine(name);
    }
}
catch (DataServiceQueryException ex)
{
    QueryOperationResponse response = ex.Response;

    Console.WriteLine(response.Error.Message);
}
Calling a service operation that returns no data
How to do it
// Define the query URI to access the service operation,
// relative to the service URI.
string queryString = "ReturnsNoData";

// Create the DataServiceContext using the service URI.
TrainingEntities context = new TrainingEntities(new Uri("http://localhost/TrainingService1/"));

try
{
    // Execute a service operation that returns void.
    context.Execute<string>(new Uri(queryString, UriKind.Relative));
}
catch (DataServiceQueryException ex)
{
    QueryOperationResponse response = ex.Response;

    Console.WriteLine(response.Error.Message);
}
Calling a service operation asynchronously using Execute<T>
How to do it

Invoking method:

// Define the service operation query parameter.
string city = "Lisboa";

// Define the query URI to access the service operation with specific query options relative to the service URI.
string queryString = string.Format("GetOrdersByCity?city='{0}'", city)
    + "&$orderby=ShippedDate desc"
    + "&$expand=Order_Details";

// Create the DataServiceContext using the service URI.
TrainingEntities context = new TrainingEntities(new Uri("http://localhost/TrainingService1/"));

// Execute the service operation that returns all orders for the specified city.
var results = context.BeginExecute<Order>(
                new Uri(queryString, UriKind.Relative),
                OnAsyncExecutionComplete, context);

Method invoked when execution completes:

private static void OnAsyncExecutionComplete(IAsyncResult result)
{
    // Get the context back from the stored state.
    var context = result.AsyncState as TrainingEntities;

    try
    {
        // Complete the exection and write out the results.
        foreach (Order o in context.EndExecute<Order>(result))
        {
            Console.WriteLine(string.Format("Order ID: {0}", o.OrderID));

            foreach (Order_Detail item in o.Order_Details)
            {
                Console.WriteLine(String.Format("\tItem: {0}, quantity: {1}",
                    item.ProductID, item.Quantity));
            }
        }
    }
    catch (DataServiceQueryException ex)
    {
        QueryOperationResponse response = ex.Response;

        Console.WriteLine(response.Error.Message);
    }
}
Calling a service operation asynchronously using CreateQuery<T>
How to do it

Invoking method:

// Define the service operation query parameter.
string city = "Lisboa";

// Create the DataServiceContext using the service URI.
TrainingEntities context = new TrainingEntities(,svc/");

// Use the CreateQuery method to create a query that accessess
// the service operation passing a single parameter.
var query = context.CreateQuery<Order>("GetOrdersByCity")
    .AddQueryOption("city", string.Format("'{0}'", city))
    .Expand("Order_Details");

// Execute the service operation that returns
// all orders for the specified city.
var results =
    query.BeginExecute(OnAsyncQueryExecutionComplete, query);

Method invoked when execution completes:

private static void OnAsyncQueryExecutionComplete(IAsyncResult result)
{
    // Get the query back from the stored state.
    var query = result.AsyncState as DataServiceQuery<Order>;

    try
    {
        // Complete the execution and write out the results.
        foreach (Order o in query.EndExecute(result))
        {
            Console.WriteLine(string.Format("Order ID: {0}", o.OrderID));

            foreach (Order_Detail item in o.Order_Details)
            {
                Console.WriteLine(String.Format("\tItem: {0}, quantity: {1}",
                    item.ProductID, item.Quantity));
            }
        }
    }
    catch (DataServiceQueryException ex)
    {
        QueryOperationResponse response = ex.Response;

        Console.WriteLine(response.Error.Message);
    }
}
Extending the data service context and invoke custom service operations using Execute<T>
How to do it
namespace TrainingClient.Training
{
    public partial class TrainingEntities
    {
        public Customer CustomerByCity(string city)
        {
            if (city == null)
                throw new DataServiceClientException("CustomersByCity requires a city name");

            Uri uri = new Uri("/CustomersByCity()?city='" + city + "'", UriKind.Relative);

            return this.Execute<Customer>(uri);
        }

    }
}
Extending the data service context and invoke custom service operations using CreateQuery<T>
How to do it
namespace TrainingClient.Training
{
    public partial class TrainingEntities
    {

       public DataServiceQuery<Customer> CustomersByCity(string city)
       {
          if (city == null)
             throw new DataServiceClientException("CustomersByCity requires a city name");

          return this.CreateQuery<Customer>("CustomersByCity").AddQueryOption("city","'"+city+"'");
       }
   }
}
References

[1] Calling Service Operations (WCF Data Services)