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:
- Bound to entity type.
- 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:
- Both convention and attribute routing can be used in bound function.
- In convention routing, if the method name starts with “Get”, the [HttpGet] can be omitted.
- 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:
- Bound to entity type.
- 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:
- Each method in the controller has “ODataActionParameters” as parameter. It is a dictionary.
- Convention and attribute routing can be used in bound action.
- 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.
0 comments