Service Development: Define Service Operations and add service logic

Description and code samples
Table of Contents
Description

It´s possible to define custom operations beyond normal CRUD operations performed on the resource.

These operations can be exposed and addressable by an URI.

Custom operations are used to:

  • Add business logic to the data service
  • Add specialized querying capabilities to the data service

Service operations are methods added to the data service class that derives from DataService<T>.

Service operations can access the underlying data source through the CurrentDataSource property on the DataService<T> class

Note

In the metadata, service operations are represented as a FunctionImport OData element.

Operations
Definition Requirements
How to do it

In order to have a method exposed as a service data operation the following conditions must be met:

  • The operation must be a public method of the data service class;
  • The operation method only accept input parameters: If parameters are defined, the type of each parameter must be a primitive type. Any data of a non-primitive type must be serialized and passed into a string parameter.

and the return type value must be:

Return Type Remarks
void
IEnumerable<T> The URI must be a single path segment that is the name of the service operation
IQueryable<T> Required to:

  • support query options as sorting, paging, and filtering (Query path segments in the URI in addition to the path that is the name of the service operation);
  • support accessing related entities by using navigation properties (Query path segments in the URI in addition to the path that is the name of the service operation);
entity type An entity type in the data model that the data service exposes
primitive type An entity type in the data model that the data service exposes

The method must be annotated with the [WebGet] or [WebInvoke] attribute:

  • [WebGet] enables the method to be invoked by using a GET request;
  • [WebInvoke(Method = "POST")] enables the method to be invoked by using a POST request

Note: If the method return a single result it should be annotated with the [SingleResultAttribute]. This will have reflection on the response serialization and the manner in which additional navigation property traversals are represented in the URI.

 

Address custom service operations
How to do it

The first path segment represents the name of the method
The next path segment represents navigation properties
The parameters of the method are sent as query options in the query string
The data services query options represent the last segment of the URI and each option is started by the “$” character

Samples:

http//localhost:12345/TraningService.svc/GetOrdersByCity?city='London'&$expand=Order_Details&$orderby=RequiredDate

http://localhost:12345/TraningService.svc/GetOrdersByCity?city='London'

http://localhost:12345/TraningService.svc/GetOrdersByCity?city='London'&$top=2

http://localhost:12345/TraningService.svc/GetOrdersByCity?city='London'&$expand=Order_Details&$orderby=RequiredDate desc
Configuration
How to do it

Service-wide visibility of service operations is controlled by the SetServiceOperationAccessRule method on the IDataServiceConfiguration class

Sample:

config.SetServiceOperationAccessRule("GetOrdersByCity", ServiceOperationRights.AllRead);
Raising Exceptions
How to do it

Sample:

public class WcfDataService1 : DataService<TrainingContext>
{
	[WebGet]
	public IQueryable<Order> GetOrdersByCity(string city)
	{
		if (string.IsNullOrEmpty(city))
		{
			throw new ArgumentNullException("city",
				"A value must be provided for the parameter'city'.");
		}

		// Get the ObjectContext that is the data source for the service.
		TrainingContext ctx = this.CurrentDataSource;

		try
		{
			var query = from order in ctx.Orders.Include("Order_Details")
						where order.Customer.City == city
						select order;

			return query;
		}
		catch (Exception ex)
		{
			throw new ApplicationException(string.Format(
				"An error occurred: {0}", ex.Message));
		}
	}
}