December 8th, 2014

[Tutorial & sample] Functions & Actions in Web API V2.2 for OData V4.0 – Type Scenario

Sam Xu
Senior Software Engineer

Introduction

Functions and actions are two of the most important parts in OData. They are always very useful to define special/customized server-side behaviors to process the data in OData services.

From OData V4 spec, functions and actions both are operations and can be either bound to a type or unbound. However, there’s a little bit difference between them:

  • Functions are operations that do not have side effects, may support further composition and must have return type.
  • Actions are operations that allow side effects, such as data modification, and cannot be further composed in order to avoid non-deterministic behavior.

In Web API V2.2 for OData V4.0, functions and actions are supported, for both bound and unbound. This blog is intended to use detail examples to illustrate how to configure/route/invoke functions and actions, for both bound and un-bound, in Web API V2.2 for OData with strong CRL types. For basic tutorial of function and action, you can refer to Actions and Functions in OData v4 using ASP.NET Web API 2.2.

Ok, let’s get started.

Construct the model

For simplicity, we start by creating a simple model which can be used to simulate a simple 2D drawing system.

Define CLR classes

First of all, define the CLR class Graph, Shape, Circle, Rectangle, Triangle, Point and enum type ShapeType. The detailed properties and relationships between each items are shown in the following picture:

Where:

  • Graph serves as an entity type, which has a navigation property named “Shapes”.
  • Shape serves as an abstract entity type,
  • Circle, Rectangle and Triangle are subclasses derived from Shape.
  • Point serves as a complex type.
  • ShapeType serves as an enum type.

Build Edm Model

Based on CLR classes and enum type, Edm Model has to be built explicitly on the conventional model builder. The codes are as follows:

Where:

  • Graphs and Shapes are two entity sets.
  • BuildFunctions(builder): is a private method in which Functions will be built. We will add more functions in this method later.
  • BuildActions(builder): is a private method in which Actions will be built. We will add more actions in this method later.

Create OData Controller

At the beginning, we create a simple controller that just have one method, and use in-memory lists to store entities.

Where, MapContext plays the DB Role, in which has two lists (Graphs and Shapes):

Just for test, we create 4 graphs and 10 shapes in BuildMap() function (detail codes can be found in attached file.)

Functions in Web API

As mentioned, functions have the following main features:

  • Do not have side effects
  • Can be composed
  • Must have return type

Besides, Functions can be either bound to a type or unbound.

Bound functions

Basically, there are two different kinds of bound functions:

  1. Bound to entity type.
  2. Bound to collection of entity type.

Bound function can be overloaded, but for all bound overload functions with same binding parameter type, must have same return type. Let’s see how to configure/route/invoke the bound functions:

Configure bound functions

Add the following bound functions in the BuildFunctions(builder). The first four are functions bound to collection of entity type with overload, while the other two are functions bound to entity type.

Note:

The parameter string value of Returns{Collection|Entity}ViaEntitySetPath() should follow up the template as:

“bindingParameter/{NavigationPropertyName}/{NavigationPropertyName}/…”.

Route bound functions

In the GraphsController, add the following methods for bound function routing:

Note:

  1. Both convention and attribute routing can be used in bound function.
  2. In convention routing, if the method name starts with “Get”, the [HttpGet] can be omitted.
  3. In attribute routing, it does not matter to which controller your method belongs and what method name you create. Please refer to the method “PointInGraph”.

Invoke bound functions

To invoke a bound function, the client issues a GET request with namespace-qualified function name to a URL that identifies a resource whose type is the same as, or is derived from, the type of the binding parameter of the function. (Please refer to here to resolve the problem when using the default IIS).

Let’s issue two requests as examples:

GET ~/odata/Graphs/Default.GetShapeCount()

GET ~/odata/Graphs/Default.GetShapeCount(shapeType=FunctionActionBlog.ShapeType’Circle’)

Then, the responses are as below respectively:

Below are other examples to invoke the bound functions:

  • GET ~/odata/Graphs/Default.GetShapeCount()
  • GET ~/odata/Graphs/Default.GetShapeCount(shapeType=FunctionActionBlog.ShapeType’Circle’)
  • GET ~/odata/Graphs/Default.LeastAreaShape()
  • GET ~/odata/Graphs/Default.LeastAreaShape(shapeType=FunctionActionBlog.ShapeType’Rectangle’)
  • GET ~/odata/Graphs(2)/Default.IsPointInGraph(ptX=215,ptY=115)
  • GET ~/odata/Graphs(2)/Default.ShapeAreaLt(area=1000.0)
  • GET ~/odata/Graphs(2)/Default.ShapeAreaLt(area=1000.0)/$count

Unbound functions

Unbound functions don’t bound to any type and they are called as static operations. All unbound function overloads MUST have same return type.

Configure unbound functions

In Web API, unbound functions are configured on the model builder directly. Let’s add the following two unbound functions in the BuildFunctions(builder). The first one is an unbound function which returns collection of primitive types and the other one is an unbound function which returns from entity set.

Note:

For unbound function, just use the entity set name as the argument to call ReturnsFromEntitySet<T>(entitySetName);

Route unbound functions

Only attribute routing can be used for unbound function. Let’s add the following methods for unbound functions routing in the GraphsController (or any other controllers):

Invoke unbound functions

Unbound functions must be called through function import from the service root. So, to invoke a function through a function import, the client issues a GET request to the service root, followed by the name of the function import.

Let’s issue a request as example:

GET ~/odata/MapBoundary

Then, the response is as below:

Actions in Web API

As mentioned, Actions have the following main features:

  • Be allowed to have side effects
  • Can not be composed
  • May have return type

Besides, same as functions, actions can be either be bound to a type or unbound.

Bound actions

Same as bound functions, there are two different kinds of bound actions:

  1. Bound to entity type.
  2. Bound to collection of entity type.

However, the overload of bound actions are different. Bound actions can be overloaded, but overload must happen by different binding parameter. For one binding parameter, there can be only one bound action.

Configure bound actions

Let’s add the following bound actions in the BuildActions(builder). The first two are actions bound to collection of entity type, the other two are actions bound to entity type.

Route bound actions

In the GraphsController, add the following methods for bound action routing:

Note:

  1. Each method in the controller has “ODataActionParameters” as parameter. It is a dictionary.
  2. Convention and attribute routing can be used in bound action.
  3. In attribute routing, it does not matter to which controller your method belongs and what method name you create. Refer to the method “ChangeCircleRadius”.

Invoke bound actions

To invoke a bound action, the client can issue a POST request with namespace-qualified action name to a URL that identifies a resource whose type is same as, or is derived from the type of the binding parameter of the action. (Please refer to here to resolve the problem when using the default IIS).

Let’s issue a requests as example:

Here’s the debug information:

Unbound actions

Unbound actions don’t bound to any type and they are called as static operations same as unbound functions. However, unbound actions do not allow overloading.

Configure unbound actions

Unbound actions are configured, same as unbound function, directly in the model builder. Let’s add the following unbound actions in the BuildActions(builder).

Route unbound actions

Only attribute routing can be used for unbound action. In the GraphsController or any other controllers, add the following methods for bound actions routing:

Invoke unbound actions

To invoke an action through an action import, the client issues a POST request to the service root, followed by the name of the action import.

For example, Let’s issue a request as:

Here’s the debug information:

Conclusion

This blog uses the detailed examples to illustrate how to configure/route/invoke bound and unbound functions and actions in Web API 2.2 for OData V4. From these examples, we can see that the functions and actions shipped with Web API provides a very easy and useful way for customers to add their special and customized server-side behaviors to their OData service. Besides, we do believe that, along with the entity type, complex type and the collection be supported as function parameter in the next release of Web API OData, it will surely expand the usage of Web API OData in the future. Please download the attached file to find the source codes mentioned in this blog.Thanks.

FunctionActionBlog.zip

Category
OData

Author

Sam Xu
Senior Software Engineer

Sam is a Senior software engineer at Microsoft with over than 10 years of software developement experience. He's worked on a wide variety of platforms such as (C++, C#, etc.) and currently works on the OData team to design and implement features in the .NET stack of Microsoft's OData libraries. OData (Open Data Protocol) is an ISO/IEC approved, OASIS standard that defines a set of best practices for building and consuming RESTful APIs. You can find more information about OData at ...

More about author

0 comments

Discussion are closed.