1. INTRODUCTION
-
1.1 Preface
About
This document aims to provide organized content about how to build a OData V4 service using ASP.NET Web API for OData. It’s more than just a getting started guidance and is more systematic than the samples.
Version
This is the first version of this document written in April, 2015.
Targetted audience
This document fits best the readers who has a relative good knowledge of OData (e.g. knowing the OData primitive and structured types, knowing the basic OData URL conventions, knowing the basic OData features such as operations, queries and so on) and would like to explore how some advanced scenarios can be implemented using Web API for OData.
Beginners to OData or Web API for OData can also leverage this document as a structured way to learn. But it’s strongly recommended to read the Getting Started tutorials on OData.org to get a grasp of OData concepts before reading this doc.
This document also assumes that the readers know how to create projects in Visual Studio and know how to install packages using the Nuget Package Manager. It also assumes they have knowledge in C# programming and are not unfamiliar with concepts like classes, properties, methods, and so on.
Structure of this document
This document starts with a tutorial about how a simplest OData V4 service can be written using ASP.NET Web API for OData. Then it steps into the section about how OData models can be built in different ways. After that, OData routing is introduced in details followed by a description of OData feature implementation. Finally, it talks about security and customization of the OData V4 service.
Resources and references
-
1.2 Write a simple OData V4 service
Let’s get started by creating a simple OData V4 service. It has one entity set
Products
, one entity typeProduct
.Product
has two propertiesID
andName
, withID
being an integer andName
being a string. The service is read only. The only data clients can get besides the service document and metadata document, is theProducts
entity set.a. Create the Visual Studio project
In Visual Studio, create a new C# project from the ASP.NET Web Application template. Name the project “ODataService”.
In the New Project dialog, select the Empty template. Under “Add folders and core references…”, click Web API. Click OK.
b. Install the OData packages
In the Nuget Package Manager, install
Microsoft.AspNet.OData
and all it’s dependencies.c. Add a model class
Add a C# class to the Models folder:
d. Add a controller class
Add a C# class to the Controllers folder:
In the controller, we defined a
List<Product>
object which has one product element. It’s considered as a in-memory storage of the data of the OData service.We also defined a
Get
method that returns the list of products. The method refers to the handling of HTTP GET requests. We’ll cover that in the sections about routing.e. Configure the OData Endpoint
Open the file App_Start/WebApiConfig.cs. Replace the existing
Register
method with the following code:f. Start the OData service
Start the OData service by running the project and open a browser to consume it. You should be able to get access to the service document at
http://host/service/
in whichhttp://host/service/
is the root path of your service. The metadata document can be accessed atGET http://host/service/$metadata
and the products atGET http://host/service/Products
.
2. DEFINING THE MODEL
-
2.1 Introduction to the model builders
The data model is the basis of an OData service. OData service uses an abstract data model called Entity Data Model (EDM) to describe the exposed data in the service. OData client can issue a GET request to the root URL of the OData service with
$metadata
to get an XML representation of the service’s data model. In Microsoft ASP.NET Web API 2.2 for OData v4.0, to build a data model for OData service is to create anIEdmModel
object. There are three ways to build an EDM model in Web API OData:- Explicit Edm Model Builder
- Implicit, non-convention model builder or fluent API
- Implicit, convention model builder.
2.1.1 Build Edm Model
Let’s see the difference between them.
Explicit model builder
To build an Edm model explicitly is to create an
IEdmModel
object by directly using APIs in ODatalib. The basic code structure to build an Edm model explicitly is shown as:The Edm Model built by this way is called un-typed (or typeless, week type) Edm model. Owing that there is no corresponding CLR classes.
Non-convention model builder
To build an Edm model using non-convention model builder is to create an
IEdmModel
object by directly call fluent APIs ofODataModelBuilder
. The developer should take all responsibility to add all Edm types, operations, associations, etc into the data model one by one. The basic code structure of this way is shown as:Convention model builder
To build an Edm model using convention model builder is to create an
IEdmModel
object by a set of conventions. Such conventions are pre-defined rules in Web API OData to help model builder to identify Edm types, keys, association etc automatically, and build them into the final Edm model.ODataConventionModelBuilder
wrappers these conventions and apply them to the Edm model when building. The basic code structure of this way is shown as:Basically, it’s recommended to use convention model builder to build Edm model for its simplicity and convenience. However, if user wants to make more control on the model building, or he doesn’t have the corresponding CLR classes, the non-convention model builder and the explicitly method are also very useful.
2.1.2 Edm Model Sample
We’ll build an Edm model using the above three methods in the following sections respectively. Each section is designed to walk you through every required aspect to build such Edm model. First of all, let’s have a brief view about the Edm model we will build. This is a Customer-Order business model, in which three entity types, two complex types and one enum type are included. Here’s the detail information about each types:
• Customer is served as an entity type with three properties.
• VipCustomer is an entity type derived from Customer. It includes one more property:
• Order is another entity type with two properties.
• Address & SubAddress are served as complex types, while SubAddress is derived from Address.
• Color is served as an Enum Type.
Here’s the class heritance:
-
2.2 Build Edm Model Explicitly
As mentioned in previous section, to build Edm model explicitly is to create an
IEdmModel
object directly using ODatalib API. The Edm model built by this method is called type-less model, or week type model, or just un-typed model.Let’s see how to build the Customer-Order business model.
Enum Type
We can use
EdmEnumType
to define an Enum typeColor
as:It will generate the below metadata document:
Complex Type
Basic Complex Type
We can use
EdmComplexType
to define a complex typeAddress
as:It will generate the below metadata document:
Derived Complex type
We can set the base type in construct to define a derived complex type
SubAddress
as:It will generate the below metadata document:
Other Complex Types
We can call the following construct to set a complex type whether it is abstract or open.
For example:
It will generate the below metadata document:
Entity Type
Basic Entity Type
We can use
EdmEntityType
to define two entity typesCustomer
&Order
as:It will generate the below metadata document:
Derived Entity type
We can set the base type in construct to define a derived entity type
VipCustomer
as:It will generate the below metadata document:
Other Entity Types
We can call the following construct to set an entity type whether it is abstract or open.
For example:
It will generate the below metadata document:
Default Entity Container
Each model MUST define at most one entity container, in which entity sets, singletons and operation imports are defined. For example:
It will generate the below metadata document:
Singleton
We can also add singleton into entity container. For example:
It will generate the below metadata document:
Navigation Property
Now, we can add navigation property to Customer. For example:
It will generate the below metadata document:
First, it will add a new item in the entity type as:
Second, it will add a new item in the entity container for Customers entity set as:
Function
Let’s define two functions. One is bound, the other is unbound as:
It will generate the below metadata document:
Action
Let’s define two actions. One is bound, the other is unbound as:
It will generate the below metadata document:
Function Import
Unbound function can be called through function import. The following codes are used to build a function import:
It will generate the below metadata document:
Action Import
Unbound actioin can be called through action import. The following codes are used to build an action import:
It will generate the below metadata document:
Summary
Let’s put all codes together:
And the final XML will be:
-
2.3 Non-convention model builder
To build an Edm model using non-convention model builder is to create an
IEdmModel
object by directly call fluent APIs ofODataModelBuilder
. The developer should take all responsibility to add all Edm types, operations, associations, etc into the data model one by one. Let’s see how to build the Ccustomer-Order* business model byODataModelBuilder
.CLR Models
Non-convention model builder is based on CLR classes to build the Edm Model. The Customer-Order business CLR classes are present in abstract section.
Enum Type
The following codes are used to add an Enum type
Color
:It will generate the below metadata document:
Complex Type
Basic Complex Type
The following codes are used to add a complex type
Address
:It will generate the below metadata document:
Derived Complex type
The following codes are used to add a derived complex type
SubAddress
:It will generate the below metadata document:
Abstract Complex type
The following codes are used to add an abstract complex type:
It will generate the below metadata document:
Open Complex type
In order to build an open complex type, you should change the CLR class by adding an
IDictionary<string, object>
property, the property name can be any name. For example:Then you can build the open complex type by call
HasDynamicProperties()
:It will generate the below metadata document:
You can find that the complex type
Address
only has two properties, while it hasOpenType="true"
attribute.Entity Type
Basic Entity Type
The following codes are used to add two entity types
Customer
&Order
:It will generate the below metadata document:
Abstract Open type
The following codes are used to add an abstract entity type:
It will generate the below metadata document:
Open Entity type
In order to build an open entity type, you should change the CLR class by adding an
IDictionary<string, object>
property, while the property name can be any name. For example:Then you can build the open entity type as:
It will generate the below metadata document:
You can find that the entity type
Customer
only has three properties, while it hasOpenType="true"
attribute.Entity Container
Non-convention model builder will build the default entity container automatically. However, you should build your own entity sets as:
It will generate the below metadata document:
Besides, you can call
Singleton<T>()
to add singleton into entity container.Function
It’s very simple to build function (bound & unbound) in Web API OData. The following codes define two functions. The first is bind to
Customer
, the second is unbound.It will generate the below metadata document:
Besides, Web API OData will automatically add function imports for all unbound functions. So, the metadata document should has:
Action
Same as function, it’s also very simple to build action (bound & unbound) in Web API OData. The following codes define two actions. The first is bind to collection of
Customer
, the second is unbound.It will generate the below metadata document:
Same as function, Web API OData will automatically add action imports for all unbound actions. So, the metadata document should has:
Summary
Let’s put all codes together:
And the final XML will be:
-
2.4 Convention model builder
In the previous two sections, we walk you through the required aspects to build an Edm model by directly using ODatalib or leveraging
ODataModelBuilder
fluent API in WebApi OData.Obvious, there are many codes you should add to develop a simple Customer-Order business model. However, Web API OData also provides a simple method by using
ODataConventionModelBuilder
to do the same thing. It’s called convention model builder and can extremely reduce your workload.Convention model builder uses a set of pre-defined rules (called conventions) to help model builder identify Edm types, keys, associations, relationships, etc automatically, and build them into the final Edm data model.
In this section, we will go through all conventions used in convention model builder. First, let’s see how to build the Edm model using
ODataConventionModelBuilder
.CLR Models
We also use the Customer-Order business model presented in abstract section.
Build the Edm Model
The following codes can add all related entity types, complex types, enum type and the corresponding entity sets into the Edm model:
It will generate the below metadata document:
Note: We omit the function/action building because it’s same as non-convention model builder.
Conventions
Wow, how the convention model builder do that! Actually, convention model builder uses a set of pre-defined rules (called conventions) to achieve this. If you open the source code for
ODataConventionModelBuilder
, You can find the following codes at the beginning of theODataConventionModelBuilder
class:Where lists the conventions wrapped in convention model builder. However, in
ODataConventionModelBuilder
, there are some conventions which can’t be clearly listed. Let’s walk you through these conventions one by one with some relevant attributes & annotations to illustrate the convention model builder.Type Inheritance Identify Convention
Rule: Only derived types can be walked.
For example:
By using convention builder:
It will generate the below entity type in the resulted EDM document:
If you change the model builder as:
It will generate the below entity type in the resulted EDM document:
There is no
Base
entity type existed.Abstract type convention
Rule: The Edm type will be marked as abstract if the class is abstract.
The result is :
Entity key convention
Rule: If one and only one property’s name is ‘Id’ or ‘
<entity class name>
Id’ (case insensitive), it becomes entity key property.The result is:
Key attribute
Rule: The [
KeyAttribute
] specifies key property, it forces a property without ‘id’ to be Key property. It’ll suppress the entity key convention.The result is:
ComplexType Attribute Convention
Rule-1: Create a class without any ‘id’ or ‘
id’ or [`KeyAttribute`] property, like Rule-2: Add [ComplexType] attribute to a model class: it will remove ‘id’ or ‘
id’ or [Key] properties, the model class will have no entity key, thus becomes a complex type. Then, the above two types wil be built as
Complex Type
.DataContract & DataMember
Rule: If using DataContract or DataMember, only property with [DataMember] attribute will be added into Edm model.
The resulted EDM document is:
You can also change name-space and property name in EDM document. For example, if the above DataContract attribute is added with NameSpace:
The result will become:
NotMapped Attribute Convention
Rule: [NotMapped] deselects the property to be serialized or deserialized, so to some extent, it can be seen as the converse of DataContract & DataMember.
For example, the above if
Trip
class is changed to the below, it generates exactly the same Trip Entity in EDM document, that is, no ‘SharedId’ property.The result is:
Required Attribute Convention
Rule: The property with [Required] attribute will be non-nullable.
Then the result has
Nullable=”false”
forName
property:ConcurrencyCheck Attribute Convention
Rule: It can mark one or more properties for doing optimistic concurrency check on entity updates.
The expected result should be like the below:
Timestamp Attribute Convention
Rule: It’s same as [ConcurrencyCheck].
The expected result should be like the below:
IgnoreDataMember Attribute Convention
Rule: It has the same effect as
[NotMapped]
attribute. It is able to revert the[DataMember]
attribute on the property when the model class doesn’t have[DataContract]
attribute.NonFilterable & NotFilterable Attribute Convention
Rule: Property marked with [NonFilterable] or [NotFilterable] will not support
$filter
query option.Then, if you issue an query option as:
~/odata/Customers?$filter=Title eq 'abc'
You will get the following exception:
NotSortable & Unsortable Attribute Convention
Rule: Property marked with [NotSortable] or [Unsortable] will not support
$orderby
query option.Then, if you issue an query option as: `~/odata/Customers?$orderby=Title
You will get the following exception:
NotNavigable Attribute Convention
Rule: Property marked with [NotNavigable] will not support
$select
query option.Then, if you issue an query option as: `~/odata/Customers?$select=address
You will get the following exception:
NotExpandable Attribute Convention
Rule: Property marked with [NotExpandable] will not support
$expand
query option.Then, if you issue an query option as: `~/odata/Customers?$expand=Orders
You will get the following exception:
NotCountable Attribute Convention
Rule: Property marked with [NotCountable] will not support
$count
query option.Then, if you issue an query option as: `~/odata/Customers(1)/Addresses?$count=true
You will get the following exception:
ForeignKey Attribute Convention
Rule: Property marked with [ForeignKey] will be used to build referential constraint.
You will get the following result:
[ForeignKey] can also put on dependent property. for example:
It’ll get the same result.
ActionOnDelete Attribute Convention
Rule: Property marked with [ActionOnDelete] will be used to build referential constraint action on delete.
You will get the following result:
ForeignKeyDiscovery Convention
Rule: A convention used to discover foreign key properties if there is no any foreign key configured on the navigation property. The basic rule to discover the foreign key is: with the same property type and follow up the naming convention. The naming convention is: 1. The “Principal class name + principal key name” equals the dependent property name For example: Customer (Id) <–> Order (CustomerId) 2. or the “Principal key name” equals the dependent property name. For example: Customer (CustomerId) <–> Order (CustomerId)
You will get the following result:
-
2.5 Summary
In this chapter, we describe the three methods used to build the Edm model in Web API OData and walk you through every required aspect to build a simple Customer-Order Edm model. It’s recommended to use convention model builder to build Edm model for its simplicity and convenience. However, if user wants to make more control on the model building, or he doesn’t have the corresponding CLR classes, the non-convention model builder and the explicitly method are also very useful.
3. ROUTING
-
3.1 Introduction Routing
In Web API, Routing is how it matches a request URI to an action in a controller. The Routing of Web API OData is derived from Web API Routing and do more extensions. In Web API OData, an OData controller (not API controller) is severed as the request handler to handle HTTP requests, while the public methods (called action methods) in the controller are invoked to execute the business logic. So, when the client issues a request to OData service, the Web API OData framework will map the request to an action in the OData controller. Such mapping is based on pre-registered Routes in global configuration.
Register the Web API OData Routes
In Web API, developer can use the following codes to register a Web API route into routing table:
While, Web API OData re-uses the Web API routing table to register the Web OData Routes. However it provides its own extension method called
MapODataServiceRoute
to register the OData route.MapODataServiceRoute
has many versions, here’s the basic usage:With these codes, we register an OData route named “myRoute”, uses “odata” as prefix and by calling
GetEdmModel()
to set up the Edm model.After registering the Web OData routes, we define an OData route template in the routing table. The route template has the following syntax:
Now, the Web API OData framework can handle the HTTP request. It tries to match the request Uri against one of the route templates in the routing table. Basically, the following URIs match the odata route:
Where, Customers is the entity set names.
However, the following URI does not match the odata route, because it doesn’t match “odata” prefix segment:
Routing Convention
Once the odata route is found, Web API OData will parse the request Uri to get the path segments. Web API OData first uses the ODatalib to parse the request Uri to get the ODL path segments, then convert the ODL path segments to Web API OData path segments. Once the Uri Parse is finished, Web API OData will try to find the corresponding OData controller and action. The process to find controller and action are the main part of Routing Convention. Basically, there are two parts of Routing Convention:
-
Convention Routing
It is also called built-in routing conventions. It uses a set of pre-defined rules to find controller and action.
-
Attribute Routing
It uses two Attributes to find controller and action. One is
ODataRoutePrefixAttribute
, the other isODataRouteAttribute
.
-
-
3.2 Built-in routing conventions
When Web API gets an OData request, it maps the request to a controller name and an action name. The mapping is based on the HTTP method and the URI. For example,
GET /odata/Products(1)
maps toProductsController.GetProduct
.This article describe the built-in OData routing conventions. These conventions are designed specifically for OData endpoints, and they replace the default Web API routing system. (The replacement happens when you call MapODataRoute.)
Built-in Routing Conventions
Before describe the OData routing conventions in Web API, it’s helpful to understand OData URIs. An OData URI consists of:
- The service root
- The odata path
- Query options
For example:
http://example.com/odata/Products(1)/Supplier?$top=2
- The service root : http://example.com/odata
- The odata path : Products(1)/Supplier
- Query options : ?$top=2
For OData routing, the important part is the OData path. The OData path is divided into segments, each segments are seperated with ‘/’.
[For example],
Products(1)/Supplier
has three segments:- Products refers to an entity set named “Products”.
- 1 is an entity key, selecting a single entity from the set.
- Supplier is a navigation property that selects a related entity.
So this path picks out the supplier of product 1.
OData path segments do not always correspond to URI segments. For example, “1” is considered a key path segment.
Controller Names. The controller name is always derived from the entity set at the root of the OData path. For example, if the OData path is
Products(1)/Supplier
, Web API looks for a controller named ProductsController.So, the controller convention is: [entityset name] + “Controller”, derived from
ODataController
Action Names. Action names are derived from the path segments plus the entity data model (EDM), as listed in the following tables. In some cases, you have two choices for the action name. For example, “Get” or “GetProducts”.
Querying Entities
Creating, Updating, and Deleting Entities
Operation on Navigation Property
Querying, Creating and Deleting Links
Properties
Request Example URI Action Name Example Action GET /entityset(key)/property /Products(1)/Name GetPropertyFromEntityType or GetProperty GetNameFromProduct GET /entityset(key)/cast/property /Products(1)/Models.Book/Author GetPropertyFromEntityType or GetProperty GetTitleFromBook Actions
Action only supports the POST request method, and the parameters are sent using the request body. In controller, each action is using an
ODataActionParameters
to accept the parameters’ value:Functions
Functions only supports the GET request method.
Method Signatures
Here are some rules for the method signatures:
- If the path contains a key, the action should have a parameter named key.
- If the path contains a key into a navigation property, the action should have a parameter named relatedKey.
- POST and PUT requests take a parameter of the entity type.
- PATCH requests take a parameter of type Delta, where T is the entity type.
For reference, here is an example that shows method signatures for most built-in OData routing convention.
Update form Routing Conventions in OData V3.0
-
3.3 Attribute Routing
Same as Web API, Web API OData supports a new type of routing called attribute routing. It uses two Attributes to find controller and action. One is
ODataPrefixAttribute
, the other isODataRouteAttribute
.You can use attribute routing to define more complex routes and put more control over the routing. Most important, it can extend the coverage of convention routing. For example, you can easily use attribute routing to route the following Uri:
In Web API OData, attribute routing is combined with convention routing by default.
Enabling Attribute Routing
ODataRoutingConventions
provides two methods to register routing conventions:As the name implies, the first one creates a mutable list of the default OData routing conventions with attribute routing enabled, while the second one only includes convention routing.
In fact, when you call the basic
MapODataServiceRoute
, it enables the attribute routing by default as:However, you can call other version of
MapODataServiceRoute
to custom your own routing conventions. For example:ODataRouteAttribute
ODataRouteAttribute
is an attribute that can, and only can be placed on an action of an OData controller to specify the OData URLs that the action handles.Here is an example of an action defined using an
ODataRouteAttribute
:With this attribute, Web API OData tries to match the request Uri with
Customers({id})/Address/City
routing template toGetCityOfACustomer()
function inMyController
. For example, the following request Uri will invokeGetCityOfACustomer
:For the above request Uri,
id
in the function will have1
,2
and301
value.However, for the following request Uri, it can’t match to `GetCityOfACustomer()’:
Web API OData supports to put multiple
ODataRouteAttribute
on the same OData action. For example,ODataRoutePrefixAttribute
ODataRoutePrefixAttribute
is an attribute that can, and only can be placed on an OData controller to specify the prefix that will be used for all actions of that controller.ODataRoutePrefixAttribute
is used to reduce the routing template inODataRouteAttribute
if all routing template in the controller start with the same prefix. For example:Then you can use
ODataRoutePrefixAttribute
attribute on the controller to set a common prefix.Now, Web API OData supports to put multiple
ODataRoutePrefixAttribute
on the same OData controller. For example,Route template
The route template is the route combined with
ODataRoutePrefixAttribute
andODataRouteAttribute
. So, for the following example:The
GetAddress
matches toCustomers({id})/Address
route template. It’s called key template because there’s a template{id}
. So far in Web API OData, it supports two kind of templates:- key template, for example:
- function parameter template, for example:
Web API OData team also works to add the third template, that is the dynamic property template. It’s planed to ship in next release.
You can refer to this blog for attribute routing in Web API 2.
-
3.4 Custom routing convention
It’s easy to custom your own routing convention to override the default Web API OData routing convention. Let’s see how to target it.
Property access routing convention
From built-in routing convention section, we know that users should add many actions for every property access.
For example, if the client issues the following property access request Uris:
Service should have the following actions in
CustomersController
to handle:If
Customer
has hundreds of properties, users should add hundres of similar functions inCustomersController
. It’s boring and we can create our own routing convention to override it.Custom routing convention
We can create our own routing convention class by implementing the
IODataRoutingConvention
. However, if you don’t want to change the behaviour to find the controller, the new added routing convention class can derive from `NavigationSourceRoutingConvention’.Let’s build a sample property access routing convention class derived from
NavigationSourceRoutingConvention
.Where, we routes the following path templates to a certain action named
GetProperty
.Enable customized routing convention
The following sample codes are used to enable the customized routing convention:
Where, we insert our own routing convention at the starting position to override the default Web API OData property access routing convention.
Add actions
In the
CustomersController
, only one method namedGetProperty
should be added.Samples
Let’s have some request Uri samples to test:
a)
The result is:
b)
The result is:
c)
The result is:
4. ODATA FEATURES
-
4.1 DateTime support
This sample will introduce how to support DateTime type in Web API OData V4.
Build DateTime Type
OData V4 doesn’t include DateTime as primitive type. Web API OData V4 uses DateTimeOffset to represent the DateTime. For example, if user defines a model as:
The metadata document for Customer entity type will be:
Time Zone Configuration
By Default, converting between DateTimeOffset and DateTime will lose the Time Zone information. Therefore, Web API provides a API to config the Time Zone information on server side. For example:
$filter DateTime
Since Web API OData 5.6, it supports to filter on DateTime type. For example:
$orderby DateTime
Since Web API OData 5.6, it supports to orderby on DateTime type. For example:
Thanks.
-
4.2 Referential constraint
The following sample codes can be used for Web API OData V3 & V4 with a little bit function name changing.
Define Referential Constraint Using Attribute
There is an attribute named “ForeignKeyAttribute” which can be place on:
1.the foreign key property and specify the associated navigation property name, for example:
2.a navigation property and specify the associated foreign key name, for example:
Where, Customer has two keys.
Now, you can build the Edm Model by convention model builder as:
Define Referential Constraint Using Convention
If user doesn’t add any referential constraint, Web API will try to help user to discovery the foreign key automatically. There are two conventions as follows: 1.With same property type and same type name plus key name. For example:
Where, Customer type name “Customer” plus key name “Id” equals the property “CustomerId” in the Order.
2.With same property type and same property name. For example:
Where, Property (key) “CustomerId” in the Customer equals the property “CustomerId” in the Order.
Now, you can build the Edm Model using convention model builder same as above section.
Define Referential Constraint Programmatically
You can call the new added Public APIs (HasRequired, HasOptional) to define the referential constraint when defining a navigation property. For example:
It also supports to define multiple referential constraints, for example:
Define Nullable Referential Constraint Using Convention
Currently, it doesn’t suppport to define nullable referential constraint from attribute and convention method. However, you can do it by Programmatically by calling
HasOptional()
method:For example:
Then you can get the following result:
Where,
CategoryId
is nullable while navigation propertySupplier
is nullable too.Thanks.
-
4.3 Nested $filter in $expand
OData Web API v5.5 supports nested $filter in $expand, e.g.:
.../Customers?$expand=Orders($filter=Id eq 10)
POCO classes:
With Edm model built as follows:
To Map route,
- For Microsoft.AspNet.OData, e.g., in
WebApiConfig.cs
:
- For Microsoft.AsnNetCore.OData, e.g., in
Startup.Configure((IApplicationBuilder app, IHostingEnvironment env)
method:
Controller:
Request:
http://localhost:port_number/orest/Customers?$expand=Orders($filter=Id eq 10)
Response:
- For Microsoft.AspNet.OData, e.g., in
-
4.4 Edm.Date and Edm.TimeOfDay
This sample introduces how to use the
Edm.Date
&Edm.TimeOfDay
supported in Web API OData V5.5.Build Edm Model
ODL V6.8 introduces two new primitive types. One is
Edm.Date
, the other isEdm.TimeOfDay
. Besides, it also introduces two new struct types to represent the CLR types of Edm.Date and Edm.TimeOfDay. So, developers can use the new CLR struct types to define their CLR model. For example, if user defines a model as:The metadata document for Customer entity type will be:
Build-in Functions
Along with the
Edm.Date
&Edm.TimeOfDay
, new date and time related built-in functions are supported in Web API OData V5.5.Here’s the list:
- Date
- Edm.Int32 year(Edm.Date)
- Edm.Int32 month(Edm.Date)
- Edm.Int32 day(Edm.Date)
- TimeOfDay
- Edm.Int32 hour(Edm.TimeOfDay)
- Edm.Int32 minute(Edm.TimeOfDay)
- Edm.Int32 second(Edm.TimeOfDay)
- Edm.Decimal fractionalseconds(Edm.TimeOfDay)
- DateTimeOffset
- Edm.Decimal fractionalseconds(Edm.DateTimeOffset)
- Edm.Date date(Edm.DateTimeOffset)
- Edm.TimeOfDay time(Edm.DateTimeOffset)
Query examples
Let’s show some query request examples:
- Date
- ~/odata/Customers?$filter=year(Publish) eq 2015
- ~/odata/Customers?$filter=month(Publish) ne 11
- ~/odata/Customers?$filter=day(Publish) lt 8
- TimeOfDay
- ~/odata/Customers?$filter=hour(CheckTime) eq 2
- ~/odata/Customers?$filter=minute(CheckTime) ge 11
- ~/odata/Customers?$filter=second(CheckTime) lt 18
- ~/odata/Customers?$filter=fractionalseconds(CheckTime) eq 0.04
- DateTimeOffset
- ~/odata/Customers?$filter=fractionalseconds(Birthday) lt 0.04
- ~/odata/Customers?$filter=date(Birthday) lt 2015-03-23
- ~/odata/Customers?$filter=time(Birthday) eq 03:04:05.90100
Thanks.
- Date
-
4.5 Abstract entity types
Since Web API OData V5.5-beta, it is allowed to:
- define abstract entity types without keys.
- define abstract type (entity & complex) without any properties.
- define derived entity types with their own keys.
Let’s see some examples:
Entity type example:
The CLR model is shown as below:
We can use the following codes to build Edm Model:
Then, we can get the metadata document for Animal as:
Note:
- Animal is an abstract entity type without any keys and any properties
- Dog & Pig are two sub entity types derived from Animal with own keys.
However, it’s obvious that abstract entity type without keys can’t be used to define any navigation sources (entity set or singleton). So, if you try to:
you will get the following exception:
Complex type example
Let’s see a complex example. The CLR model is shown as below:
We can use the following codes to build Edm Model:
Then, we can get the metadata document for Graph as:
Where, Graph is an abstract complex type without any properties.
Thanks.
-
4.6 Function parameter support
Since Web API OData V5.5-beta, it supports the following types as function parameter:
- Primitive
- Enum
- Complex
- Entity
- Entity Reference
- Collection of above
Let’s see how to build and use the above types in function.
CLR Model
First of all, we create the following CLR classes as our model:
Build Edm Model
Now, we can build the Edm Model as:
where, BuildFunction() is a helper function in which functions can be built.
Primitive and Collection of Primitive parameter
Configuration
In BuildFunction(), we can configure a function with
Primitive
and collection ofPrimitive
parameters:Routing
In the
CustomersController
, add the following method:Request Samples
We can invoke the function as:
Enum and Collection of Enum parameter
Configuration
In BuildFunction(), we can configure a function with
Enum
and collection ofEnum
parameters:Routing
In the
CustomersController
, add the following method :Request Samples
We can invoke the Enum function as:
Complex and Collection of Complex parameter
Configuration
In BuildFunction(), we can configure a function with
Complex
and collection ofComplex
parameters:Routing
In the
CustomersController
, add the following method :Request Samples
We can invoke the complex function as:
Entity and Collection of Entity parameter
Configuration
In BuildFunction(), we can configure a function with
Entity
and collection ofEntity
parameters:It’s better to call
EntityParameter<T>
andCollectionEntityParameter<T>
to define entity and collection of entity parameter.Routing
In the
CustomersController
, add the following method :Request Samples
We can invoke the entity function as:
However, only parameter alias is supported for entity.
Entity Reference and collection of Entity Reference parameter
In fact, we can’t build a function with entity reference as parameter. However, we can call the function with entity parameter using entity reference value. So, without any change for the
EntityFunction
, we can call as:FromODataUri
‘[FromODataUri]’ is mandatory for complex, entity and all collection. However, it is optional for Primitive & Enum. But for string primitive type, the value will contain single quotes without ‘[FromODataUri]’.
Thanks.
For un-typed scenario, please refer to untyped page.
-
4.7 Action parameter support
Since Web API OData V5.5-beta, it supports the following types as action parameter:
- Primitive
- Enum
- Complex
- Entity
- Collection of above
Let’s see how to build and use the above types in action.
CLR Model
Re-use the CLR models in function sample.
Build Edm Model
Same as build Edm Model in function sample, but change the helper function as BuildAction().
Primitive and Collection of Primitive parameter
Configuration
In BuildAction(), we can configure an action with
Primitive
and collection ofPrimitive
parameters:Routing
In the
CustomersController
, add the following method:Request Samples
We can invoke the action by issuing a Post on
~/odata/Customers/Default.PrimitiveAction
with the following request body:Enum and Collection of Enum parameter
Configuration
In BuildAction(), we can configure an action with
Enum
and collection ofEnum
parameters:Routing
In the
CustomersController
, add the following method :Request Samples
We can invoke the action by issuing a Post on
~/odata/Customers/Default.EnumAction
with the following request body:Complex and Collection of Complex parameter
Configuration
In BuildAction(), we can configure an action with
Complex
and collection ofComplex
parameters:Routing
In the
CustomersController
, add the following method :Request Samples
We can invoke the action by issuing a Post on
~/odata/Customers/Default.ComplexAction
with the following request body:Entity and Collection of Entity parameter
Configuration
In BuildAction(), we can configure an action with
Entity
and collection ofEntity
parameters:It’s better to call
EntityParameter<T>
andCollectionEntityParameter<T>
to define entity and collection of entity parameter.Routing
In the
CustomersController
, add the following method :Request Samples
We can invoke the action by issuing a Post on
~/odata/Customers/Default.EntityAction
with the following request body:Know issues
-
It doesn’t work if “null” value in the collection of entity in the payload. See detail in #100.
-
It doesn’t work if anything else follows up the collection of entity in the payload. See detail in #65
Null value
If you invoke an action with a ‘null’ action parameter value, please don’t add the parameter (for example,
"p1":null
) in the payload and leave it un-specified. However, for collection, you should always specify it even the collection is an empty collection (for example,"p1":[]
).Thanks.
For un-typed scenario, please refer to untyped page.
-
4.8 Operation paramters in untyped scenarios
In this page, we introduce the Function/Action parameter in untyped scenario. For CLR typed scenarios, please refer to Function page and Action page.
Build Edm Model
Let’s build the Edm Model from scratch:
Here’s the metadata document for this Edm Model:
Controller & Routing
Let’s add the following methods into
CustomersController
:Request Samples
Now, We can invoke the function with the entity and collection of entity parameter as:
Also, We can invoke the action by issuing a Post on
~/odata/Customers(1)/NS.EntityAction
with the following request body:For other request samples, please refer to Function page and Action page.
Unbound function/action
Unbound function and action are similiar with bound function and action in the request format. But only attribute routing can be used for unbound function/action routing.
Thanks.
-
4.9 Query by dynamic properties
Since Web API OData V5.5, it supports filter, select and orderby on dynamic properties.
Let’s see a sample about this feature.
CLR Model
First of all, we create the following CLR classes as our model:
Build Edm Model
Now, we can build the Edm Model as:
Use filter, orferby, select on dynamic property
Routing
In the
SimpleOpenCustomersController
, add the following method:Request Samples
We can query like:
-
4.10 Open type in untyped scenarios
Since Web API OData V5.5, it supports open type and dynamic property on un-type scenario, dynamic properties can be:
- Primitive Type
- Enum Type
- Complex Type
- Collection of above
Let’s see a sample about this feature.
Build un-type Edm Model
Now, we can build the Edm Model as:
If the dynamic property is not primitive type, you should declare it in model like the code above.
GET an untyped open entity with dynamic property
Routing
In the
UntypedSimpleOpenCustomersController
, add the following method:Request Samples
We can get the entity as:
POST an untyped open entity with dynamic property
Routing
In the
UntypedSimpleOpenCustomersController
, add the following method :Request Samples
You should declare the type of dynamic properties in request body. Payload:
Url:
The type of dynamic properties in un-type scenario
EdmEntityObject (Collection)
Represent an entity.
EdmComplexObject (Collection)
Represent an complex property.
EdmEnumObject (Collection)
Represent an enum property.
-
4.11 Query Options
OData defines parameters that can be used to modify an OData query, samples can be found at supporting-odata-query-options and using-select-expand-value.
-
4.12 Batch Support
Batch requests allow grouping multiple operations into a single HTTP request payload and the service will return a single HTTP response with the response to all operations in the requests. This way, the client can optimize calls to the server and improve the scalability of its service.
Please refer to OData Protocol for more detail about batch, and Batch in ODL for batch in ODL client.
Enable Batch in Web API OData Service
It is very easy to enable batch in an OData service which is built by Web API OData.
Add Batch Handler
As above, we only need to create a new batch handler and pass it when mapping routing for OData service. Batch will be enabled.
For testing, we can POST a request with batch body to the baseurl/$batch:
POST http://localhost:14409/odata/$batch HTTP/1.1 User-Agent: Fiddler Host: localhost:14409 Content-Length: 1244 Content-Type: multipart/mixed;boundary=batch_d3bcb804-ee77-4921-9a45-761f98d32029 --batch_d3bcb804-ee77-4921-9a45-761f98d32029 Content-Type: application/http Content-Transfer-Encoding: binary GET http://localhost:14409/odata/Products(0) HTTP/1.1 OData-Version: 4.0 OData-MaxVersion: 4.0 Accept: application/json;odata.metadata=minimal Accept-Charset: UTF-8 User-Agent: Microsoft ADO.NET Data Services --batch_d3bcb804-ee77-4921-9a45-761f98d32029 Content-Type: multipart/mixed;boundary=changeset_77162fcd-b8da-41ac-a9f8-9357efbbd --changeset_77162fcd-b8da-41ac-a9f8-9357efbbd Content-Type: application/http Content-Transfer-Encoding: binary Content-ID: 1 DELETE http://localhost:14409/odata/Products(0) HTTP/1.1 OData-Version: 4.0 OData-MaxVersion: 4.0 Accept: application/json;odata.metadata=minimal Accept-Charset: UTF-8 User-Agent: Microsoft ADO.NET Data Services --changeset_77162fcd-b8da-41ac-a9f8-9357efbbd-- --batch_d3bcb804-ee77-4921-9a45-761f98d32029 Content-Type: application/http Content-Transfer-Encoding: binary GET http://localhost:14409/odata/Products HTTP/1.1 OData-Version: 4.0 OData-MaxVersion: 4.0 Accept: application/json;odata.metadata=minimal Accept-Charset: UTF-8 User-Agent: Microsoft ADO.NET Data Services --batch_d3bcb804-ee77-4921-9a45-761f98d32029--
And the response should be:
HTTP/1.1 200 OK Cache-Control: no-cache Pragma: no-cache Content-Type: multipart/mixed; boundary=batchresponse_5667121d-ca2f-458d-9bae-172f04cdd411 Expires: -1 Server: Microsoft-IIS/8.0 OData-Version: 4.0 X-AspNet-Version: 4.0.30319 X-SourceFiles: =?UTF-8?B?YzpcdXNlcnNcbGlhbndcZG9jdW1lbnRzXHZpc3VhbCBzdHVkaW8gMjAxM1xQcm9qZWN0c1xUZXN0V2ViQVBJUmVsZWFzZVxUZXN0V2ViQVBJUmVsZWFzZVxvZGF0YVwkYmF0Y2g=?= X-Powered-By: ASP.NET Date: Wed, 06 May 2015 07:34:29 GMT Content-Length: 1449 --batchresponse_5667121d-ca2f-458d-9bae-172f04cdd411 Content-Type: application/http Content-Transfer-Encoding: binary HTTP/1.1 200 OK Content-Type: application/json; odata.metadata=minimal; charset=utf-8 OData-Version: 4.0 { "@odata.context":"http://localhost:14409/odata/$metadata#Products/$entity","ID":0,"Name":"0Name" } --batchresponse_5667121d-ca2f-458d-9bae-172f04cdd411 Content-Type: multipart/mixed; boundary=changesetresponse_e2f20275-a425-404a-8f01-c9818aa63610 --changesetresponse_e2f20275-a425-404a-8f01-c9818aa63610 Content-Type: application/http Content-Transfer-Encoding: binary Content-ID: 1 HTTP/1.1 204 No Content --changesetresponse_e2f20275-a425-404a-8f01-c9818aa63610-- --batchresponse_5667121d-ca2f-458d-9bae-172f04cdd411 Content-Type: application/http Content-Transfer-Encoding: binary HTTP/1.1 200 OK Content-Type: application/json; odata.metadata=minimal; charset=utf-8 OData-Version: 4.0 { "@odata.context":"http://localhost:14409/odata/$metadata#Products","value":[ { "ID":1,"Name":"1Name" },{ "ID":2,"Name":"2Name" },{ "ID":3,"Name":"3Name" },{ "ID":4,"Name":"4Name" },{ "ID":5,"Name":"5Name" },{ "ID":6,"Name":"6Name" },{ "ID":7,"Name":"7Name" },{ "ID":8,"Name":"8Name" },{ "ID":9,"Name":"9Name" } ] } --batchresponse_5667121d-ca2f-458d-9bae-172f04cdd411--
Setting Batch Quotas
DefaultODataBatchHandler contains some configuration, which can be set by customers, to customize the handler. For example, the following code will only allow a maximum of 8 requests per batch and 5 operations per ChangeSet.
Enable/Disable continue-on-error in Batch Request
We can handle the behavior upon encountering a request within the batch that returns an error by preference
odata.continue-on-error
, which is specified by OData V4 spec.Enable Preference
odata.continue-on-error
Preference
odata.continue-on-error
makes no sense by default, and service returns the error for that request and continue processing additional requests within the batch as default behavior.To enable
odata.continue-on-error
, please refer to section 4.20 Prefer odata.continue-on-error for details.Request Without Preference
odata.continue-on-error
For testing, we can POST a batch request without Preference
odata.continue-on-error
:POST http://localhost:9001/DefaultBatch/$batch HTTP/1.1 Accept: multipart/mixed Content-Type: multipart/mixed; boundary=batch_abbe2e6f-e45b-4458-9555-5fc70e3aebe0 Host: localhost:9001 Content-Length: 633 Expect: 100-continue Connection: Keep-Alive --batch_abbe2e6f-e45b-4458-9555-5fc70e3aebe0 Content-Type: application/http Content-Transfer-Encoding: binary GET http://localhost:9001/DefaultBatch/DefaultBatchCustomer(0) HTTP/1.1 --batch_abbe2e6f-e45b-4458-9555-5fc70e3aebe0 Content-Type: application/http Content-Transfer-Encoding: binary GET http://localhost:9001/DefaultBatch/DefaultBatchCustomerfoo HTTP/1.1 --batch_abbe2e6f-e45b-4458-9555-5fc70e3aebe0 Content-Type: application/http Content-Transfer-Encoding: binary GET http://localhost:9001/DefaultBatch/DefaultBatchCustomer(1) HTTP/1.1 --batch_abbe2e6f-e45b-4458-9555-5fc70e3aebe0--
The response should be:
HTTP/1.1 200 OK Content-Length: 820 Content-Type: multipart/mixed; boundary=batchresponse_b49114d7-62f7-450a-8064-e27ef9562eda Server: Microsoft-HTTPAPI/2.0 OData-Version: 4.0 Date: Wed, 12 Aug 2015 02:23:10 GMT --batchresponse_b49114d7-62f7-450a-8064-e27ef9562eda Content-Type: application/http Content-Transfer-Encoding: binary HTTP/1.1 200 OK Content-Type: application/json; odata.metadata=minimal; odata.streaming=true OData-Version: 4.0 { "@odata.context":"http://localhost:9001/DefaultBatch/$metadata#DefaultBatchCustomer/$entity","Id":0,"Name":"Name 0" } --batchresponse_b49114d7-62f7-450a-8064-e27ef9562eda Content-Type: application/http Content-Transfer-Encoding: binary HTTP/1.1 404 Not Found Content-Type: application/json; charset=utf-8 {"Message":"No HTTP resource was found that matches the request URI 'http://localhost:9001/DefaultBatch/DefaultBatchCustomerfoo'.","MessageDetail":"No route data was found for this request."} --batchresponse_b49114d7-62f7-450a-8064-e27ef9562eda--
Service returned error and stop processing.
Request With Preference
odata.continue-on-error
Now POST a batch request with Preference
odata.continue-on-error
:POST http://localhost:9001/DefaultBatch/$batch HTTP/1.1 Accept: multipart/mixed prefer: odata.continue-on-error Content-Type: multipart/mixed; boundary=batch_abbe2e6f-e45b-4458-9555-5fc70e3aebe0 Host: localhost:9001 Content-Length: 633 Expect: 100-continue Connection: Keep-Alive --batch_abbe2e6f-e45b-4458-9555-5fc70e3aebe0 Content-Type: application/http Content-Transfer-Encoding: binary GET http://localhost:9001/DefaultBatch/DefaultBatchCustomer(0) HTTP/1.1 --batch_abbe2e6f-e45b-4458-9555-5fc70e3aebe0 Content-Type: application/http Content-Transfer-Encoding: binary GET http://localhost:9001/DefaultBatch/DefaultBatchCustomerfoo HTTP/1.1 --batch_abbe2e6f-e45b-4458-9555-5fc70e3aebe0 Content-Type: application/http Content-Transfer-Encoding: binary GET http://localhost:9001/DefaultBatch/DefaultBatchCustomer(1) HTTP/1.1 --batch_abbe2e6f-e45b-4458-9555-5fc70e3aebe0--
Service returns the error for that request and continue processing additional requests within the batch:
HTTP/1.1 200 OK Content-Length: 1190 Content-Type: multipart/mixed; boundary=batchresponse_60fec4c2-3ce7-4900-a05a-93f180629a11 Server: Microsoft-HTTPAPI/2.0 OData-Version: 4.0 Date: Wed, 12 Aug 2015 02:27:45 GMT --batchresponse_60fec4c2-3ce7-4900-a05a-93f180629a11 Content-Type: application/http Content-Transfer-Encoding: binary HTTP/1.1 200 OK Content-Type: application/json; odata.metadata=minimal; odata.streaming=true OData-Version: 4.0 { "@odata.context":"http://localhost:9001/DefaultBatch/$metadata#DefaultBatchCustomer/$entity","Id":0,"Name":"Name 0" } --batchresponse_60fec4c2-3ce7-4900-a05a-93f180629a11 Content-Type: application/http Content-Transfer-Encoding: binary HTTP/1.1 404 Not Found Content-Type: application/json; charset=utf-8 {"Message":"No HTTP resource was found that matches the request URI 'http://localhost:9001/DefaultBatch/DefaultBatchCustomerfoo'.","MessageDetail":"No route data was found for this request."} --batchresponse_60fec4c2-3ce7-4900-a05a-93f180629a11 Content-Type: application/http Content-Transfer-Encoding: binary HTTP/1.1 200 OK Content-Type: application/json; odata.metadata=minimal; odata.streaming=true OData-Version: 4.0 { "@odata.context":"http://localhost:9001/DefaultBatch/$metadata#DefaultBatchCustomer/$entity","Id":1,"Name":"Name 1" } --batchresponse_60fec4c2-3ce7-4900-a05a-93f180629a11--
-
4.13 Delta Feed Support
Serialization Support for Delta Feed
This sample will introduce how to create a Delta Feed which is serialized into a Delta Response in Web API OData V4.
Similar to
EdmEntityObjectCollection
, Web API OData V5.6 now has anEdmChangedObjectCollection
to represent a collection of objects which can be a part of the Delta Feed. A delta response can contain new/changed entities, deleted entities, new links or deleted links.WebAPI OData V4 now has
EdmDeltaEntityObject
,EdmDeltaDeletedEntityObject
,EdmDeltaLink
andEdmDeltaDeletedLink
respectively for the objects that can be a part of the Delta response. All the above objects implement theIEdmChangedObject
interface, while theEdmChangedObjectCollection
is a collection ofIEdmChangedObject
.For example, if user defines a model as:
The
EdmChangedObjectCollection
collection for Customer entity will be created as follows:Changed or Modified objects are added as
EdmDeltaEntityObject
s:Deleted objects are added as
EdmDeltaDeletedObject
s:Delta Link is added corresponding to a $expand in the initial request, these are added as
EdmDeltaLink
s:Deleted Links is added for each deleted link that corresponds to a $expand path in the initial request, these are added as
EdmDeltaDeletedLink
s:Sample for Delta Feed
Let’s create a controller to return a Delta Feed:
Now, user can issue a GET request as:
The corresponding payload will has the following contents:
-
4.14 Capabilities vocabulary support
Web API OData supports some query limitations, for example:
- NonFilterable / NotFilterable – $filter
- NotCountable – $count
- NotExpandable – $expand
- NotNavigable – $select
- NotSortable / Unsortable – $orderby
However, the corresponding annotations cannot be exposed in metadata document. This sample introduces the capabilities vocabulary support in Web API OData V5.7, which will enable capabilites vocabulary annotations in metadata document. The related sample codes can be found here.
Build Edm Model
Let’s define a model with query limitations:
Where,
Address
is a normal complex type,Color
is an enum type andOrder
is a normal entity type. You can find their definitions in the sample codes.Based on the above CLR classes, we can build the Edm model as:
Expose annotations
Now, you can query the metadata document for capabilites vocabulary annotation as:
Thanks.
-
4.15 AutoExpand attribute
In OData WebApi 5.7, we can put
AutoExpand
attribute on navigation property to make it automatically expand withoutexpand
query option, or can put this attribute on class to make all Navigation Property on this class automatically expand.Model
Result
If you call return Product in response, Category will automatically expand and Customer will expand too. It works the same if you put
[AutoExpand]
on Class if you have more navigation properties to expand. -
4.16 Ignore query option
In OData WebApi 5.7, we can ignore some query options when calling
ODataQueryOption
ApplyTo
method, this is helpful when your odata service is integrate with other service that may already applied those query options.Customize
Controller
Result
Then your queryOption won’t apply Top and Skip.
-
4.17 Alternate keys
Alternate keys is supported in Web API OData V5.7. For detail information about alternate keys, please refer to here
The related sample codes can be found here
Enable Alternate key
Users can enable alternate key in the global configuration.
Model builder
So far, an Edm model with alternate keys can be built by ODL APIs.
So, SSN is an alternate key.
Routing for alternate key
In OData controller, Users can use the attribute routing to route the alternate key. The Uri template is similiar to function parameter. For example:
-
4.18 Add NextPageLink and $count for collection property
In OData WebApi V5.7, it supports to add the NextPageLink and $count for collection property.
Enable NextPageLink and $count
It’s easy to enable the NextPageLink and $count for collection property in controller. Users can only put the [EnableQuery(PageSize=x)] on the action of the controller. For example:
Sample Requests & Response
Request: GET http://localhost/Customers(5)/Colors?$count=true
Response content:
-
4.19 Prefer odata.include-annotations
Since OData WebApi V5.6, it supports odata.include-annotations.
odata.include-annotations
It supports the following four templates:
- odata.include-annotations=”*” // all annotations
- odata.include-annotations=”-*” // no annotations
- odata.include-annotations=”display.*” // only annotations under “display” namespace
- odata.include-annotations=”display.subject” // only annotation with term name “display.subject”
Let’s have examples:
odata.include-annotations=*
We can use the following codes to request all annotations:
The response will have all annotations:
odata.include-annotations=Entry.*
We can use the following codes to request specify annotations:
The response will only have annotations in “Entry” namespace:
-
4.20 Prefer odata.continue-on-error
Since OData Web API V5.7, it supports odata.continue-on-error.
Enable odata.continue-on-error
Users should call the following API to enable continue on error
- For Microsoft.AspNet.OData (supporting classic ASP.NET Framework):
-
For Microsoft.AspNetCore.OData (supporting ASP.NET Core):
It can be enabled in the service’s HTTP request pipeline configuration method
Configure(IApplicationBuilder app, IHostingEnvironment env)
of the typicalStartup
class:
Prefer odata.continue-on-error
We can use the following codes to prefer continue on error
The response will have all responses, includes the error responses.
-
4.21 Set namespace for operations in model builder
Since OData Web API V5.7, it allows to set a custom namespace for individual function and action in model builder.
Set namespace for function and action in model builder
The setting works for ODataConventionModelBuilder as well.
-
4.22 Use HttpRequestMessage Extension Methods
In Microsoft.AspNet.OData, set of HttpRequestMessage extension methods are provided through HttpRequestMessageExtensions. For services that don’t use LINQ or ODataQueryOptions.ApplyTo(), those extension methods can offer lots of help.
In Microsoft.AspNetCore.OData, which supports ASP.NET Core, set of HttpRequest extension methods are also provided through HttpRequestExtensions.
They are pretty much symmetrical, and the differences will be noted in the examples below. For further details, please refer to
Microsoft.AspNet.OData.Extensions.HttpRequestMessageExtensions
andMicrosoft.AspNet.OData.Extensions.HttpRequestExtensions
.ODataProperties/IODataFeature
OData methods and properties can be GET/SET through:
- ODataProperties (for Microsoft.AspNet.OData) from httpRequestMessage.ODataProperties()
- IODataFeature (for Microsoft.AspNetCore.OData) from httpRequest.ODataFeature()
Each of them includes the followings:
Path The ODataPath of the request.
PathHandler Return DefaultODataPathHandler by default.
RouteName The Route name for generating OData links.
SelectExpandClause The parsed the OData SelectExpandClause of the request.
NextLink Next page link of the results, can be set through GetNextPageLink.
For example, we may need generate service root when querying ref link of a navigation property.
GetModel
For Microsoft.AspNet.OData only, get the EDM model associated with the request.
GetNextPageLink
Create a link for the next page of results, can be used as the value of
@odata.nextLink
. For example, the request Url ishttp://localhost/Customers/?$select=Name
.Then the nextlink generated is
http://localhost/Customers/?$select=Name&$skip=10
.GetETag
Get the etag for the given request.
CreateErrorResponse
For Microsoft.AspNet.OData only, create a HttpResponseMessage to represent an error.
Then payload would be like:
{ "error":{ "code":"36", "message":"Bad stuff", "innererror":{ "message":"Exception message", "type":"", "stacktrace":"" } }
-
4.23 OData Simplified Uri convention
OData v4 Web API 5.8 RC intruduces a new OData Simplefied Uri convention that supports key-as-segment and default OData Uri convention side-by-side.
To enable the ODataSimplified Uri convention, in
WebApiConfig.cs
: -
4.24 MaxExpansionDepth in EnableQueryAttribute
Since Web API OData V5.9.1, it corrected the behavior of MaxExpansionDepth of EnableQueryAtrribute. MaxExpansionDepth means the max expansion depth for the $expand query option.
When MaxExpansionDepth value is 0, it means the check is disabled, but if you use $level=max at the same time, the expand depth will be a default value :
2
, to avoid the dead loop.Let’s see some samples about this behavior.
$expand=Manager($levels=max)
will be the same as$expand=Manager($expand=Manager)
$expand=Manager($levels=3)
will be the same as$expand=Manager($expand=Manager($expand=Manager))
Related Issue #731.
-
4.25 Bind Custom UriFunctions to CLR Methods
Since Web API OData V5.9.0, it supports to bind the custom UriFunctions to CLR methods now, so user can add,modify or override the existing pre defined built-in functions.
Let’s see how to use this feature.
Then you can use filter function like
$filter=padright(ProductName, 5) eq 'Abcd'
.Related Issue #612.
-
4.26 IN Operator
IN Operator
Starting in WebAPI OData V7.0.0 [ASP.NET ASP.NET Core], the IN operator is a supported feature that enables a shorthand way of writing multiple EQ expressions joined by OR. For example, GET /service/Products?$filter=Name eq 'Milk' or Name eq 'Cheese' or Name eq 'Donut'
can become
GET /service/Products?$filter=Name in ('Milk', 'Cheese', 'Donut')
Of the binary expression invoking IN, the left operand must be a single value and the right operand must be a comma-separated list of primitive values or a single expression that resolves to a collection; the expression returns true if the left operand is a member of the right operand.
Usage
IN operator is supported only for $filter at the moment and hardcoded collections are supported with parentheses. See examples below.
~/Products?$filter=Name in ('Milk', 'Cheese') ~/Products?$filter=Name in RelevantProductNames ~/Products?$filter=ShipToAddress/CountryCode in MyShippers/Regions ~/Products?$filter=Name in Fully.Qualified.Namespace.MostPopularItemNames
5. SECURITY
-
5.1 Basic authentication over HTTPS
We’re often asked by people if OData APIs can be secured. The name “Open Data Protocol” and the way we evangelize it (by focusing on how open a protocol it is and how it provides interoperability) may give people the impression that OData APIs doesn’t work with authentication and authorization.
The fact is that using OData is orthogonal to authentication and authorization. That is to say, you may secure an OData API in any way you can secure a generic RESTful API. We write this post to demonstrate it. The authentication methods we use in this post is the basic authentication over HTTPS. The service library we use is ASP.NET Web API for OData V4.0.
Secure an OData Web API using basic authentication over HTTPS
OData Protocol Version 4.0 has the following specification in section 12.1 Authentication:
OData Services requiring authentication SHOULD consider supporting basic authentication as specified in [RFC2617] over HTTPS for the highest level of interoperability with generic clients. They MAY support other authentication methods.
Supporting basic authentication over HTTPS is relatively easy for OData Web API. Suppose you already have a working OData service project. In this post, we implemented an OData API which has only one entity type Product and exposes only one entity set Products. In order to secure Products, the following steps needs to be taken:
1. Create a custom
AuthorizeAttribute
for the basic authenticationAdd a class to your project as follows:
In this sample we name the attribute
HttpBasicAuthorizeAttribute
. It derives fromSystem.Web.Http.AuthorizeAttribute
. We override two of its methods:OnAuthorization
andHandleUnauthorizedRequest
.In
OnAuthorization
, we first get the base64-encoded value of the headerAuthorization
and decode it. Then we apply our custom authentication logic to verify if the decoded value is a valid one. In this sample, we compare the decoded value to “Parry:123456”. As is specified in [RFC2617], this value indicates that the username is “Parry” and password is “123456”. InHandleUnauthorizedRequest
, we handle unauthorized request by responding with HTTP status code401 Unauthorized
.2. Decorate the controller with the custom
AuthorizeAttribute
We decorate our
ProductsController
withHttpBasicAuthorizeAttribute
:3. Enable HTTPS
In the project properties window, enable the SSL and remember the SSL URL:
4. Create a custom
AuthorizationFilterAttribute
for HTTPSAdd a class to your project as follows:
In this sample we name this class
RequireHttpsAttribute
. It derives fromSystem.Web.Http.Filters.AuthorizationFilterAttribute
and overrides itsOnAuthorization
method by responding with HTTP status code403 HTTPS Required
.5. Decorate the controller with the custom
AuthorizationFilterAttribute
We further decorate our
ProductsController
withRequireHttpsAttribute
:6. Testing
We run the project to test it. When run for the first time, you’ll be asked to create a self-signed certificate. Follow the instruction to create the certificate and proceed.
In the above steps, we’ve secured the OData API by allowing only HTTPS connections to the Products and responding with data only to requests that has a correct Authorization header value (the base64-encoded value of “Parry:123456”: UGFycnk6MTIzNDU2). Our HTTP service endpoint is
http://localhost:53277/
and our HTTPS endpoint ishttps://localhost:43300/
.First of all, we send a
GET
request tohttp://localhost:53277/Products
, and the service responds with an empty payload and the status code403 HTTPS Required
.Then we send the request over HTTPS to
https://localhost:43300/Products
. Since the basic authentication info needs to be provided. The service responds with an empty payload and the status code401 Unauthorized
.Finally, we set the value of the
Authorization
header to “Basic UGFycnk6MTIzNDU2” and send it over HTTPS to the same address again. The service now responds with the correct data.Summary
In this post we demoed how an OData API can be secured by basic authentication over HTTPS. You may additionally add authorization logic to the API by further customizing the
HttpBasicAuthorizeAttribute
class we created. Furthermore, you may also use other authentication methods such as OAuth2 to secure your OData API. More information can be found at: http://www.asp.net/web-api/overview/security.
6. CUSTOMIZATION
-
6.1 Custom URL parsing
Let’s show how to extend the default OData Uri Parser behavior:
Basic Case Insensitive Support
User can configure as below to support basic case-insensitive parser behavior.
Note: Case insensitive flag enables both for metadata and key-words, not only on path segment, but also on query option.
For example:
- ~/odata/$metaDaTa
- ~/odata/cusTomers …
Unqualified function/action call
User can configure as below to support basic unqualified function/action call.
For example:
Original call:
- ~/odata/Customers(112)/Default.GetOrdersCount(factor=1)
Now, you can call as:
- ~/odata/Customers(112)/GetOrdersCount(factor=1)
Since Web API OData 5.6, it enables unqualified function in attribut routing. So, Users can add the unqualified function template. For example:
Enum prefix free
User can configure as below to support basic string as enum parser behavior.
For example:
Origin call:
Now, you can call as:
Advance Usage
User can configure as below to support case insensitive & unqualified function call & Enum Prefix free:
Thanks.
-
6.2 Relax version constraints
For both Web API OData V3 and V4, a flag
IsRelaxedMatch
is introduced to relax the version constraint. WithIsRelaxedMatch = true
, ODataVersionConstraint will allow OData request to contain both V3 and V4 max version headers (V3:MaxDataServiceVersion
, V4:OData-MaxVersion
). Otherwise, the service will return response with status code 400. The default value ofIsRelaxdMatch
is false.To set this flag, API HasRelaxedODataVersionConstraint() under ODataRoute can be used as following:
-
6.3 Customize OData Formatter
This page illustrates how to use sensibility points in the ODataFormatter and plugin custom OData serializers/deserializers, gives a sample extends the ODataFormatter to add support of OData instance annotations.
Let’s see this sample.
CLR Model
First of all, we create the following CLR classes as our model:
If I search for documents by sending the search query, in the result, I’d like have a score for the match for each document, as the score is dependent on the in coming query, it cannot be modeled as a property on response’s document, it should be modeled as an annotation on the document. Let’s do that.
Build Edm Model
Now, we can build a pretty simple Edm Model as:
Customize OData Formatter
A custom entity serializer
This entity serializer is to add the score annotation (org.northwind.search.score) to document entries.
A custom serializer provider
This serializer provider is to inject the AnnotationEntitySerializer.
Setup configuration with customized ODataFormatter
Create the formatters with the custom serializer provider and use them in the configuration.
Controller Samples
You should add a score to result documents.
Request Samples
Add prefer header and send request.
Response Samples
Annotation is supported in newest night build, 5.6.0-beta1.
Conclusion
The sample has a custom entity type serializer, AnnotatingEntitySerializer, that adds the instance annotation to ODataEntry by overriding the CreateEntry method. It defines a custom ODataSerializerProvider to provide AnnotatingEntitySerializer instead of ODataEntityTypeSerializer. Then creates the OData formatters using this serializer provider and uses those formatters in the configuration.
-
6.4 Custom Stream Entity
Since Web API OData V5.7, it supports to customize entity as stream.
Fluent API
Users can call fluent API to configure the stream entity. For example,
Convention model builder
Users can put [MediaType] attribute on a CLR class to configure the stream entity. For example,
-
6.5 Customize unsupported types
ODataLib has a lot of its primitive types mapping to C# built-in types, for example,
System.String
maps toEdm.String
,System.Guid
maps toEdm.Guid
. Web API OData adds supporting for some unsupported types in ODataLib, for example:unsigned int
,unsigned long
, etc.The mapping list for the unsupported types are:
7. RELEASE NOTES
-
7.1 OData Web API 5.4 Beta
The NuGet packages for OData Web API 5.4 beta are now available on the NuGet gallery.
Download this release
You can install or update the NuGet packages for OData Web API 5.4 beta using the Package Manager Console:
PM> Install-Package Microsoft.AspNet.OData -Version 5.4.0-beta -Pre PM> Install-Package Microsoft.AspNet.WebApi.OData -Version 5.4.0-beta -Pre
What’s in this release?
This release primarily includes new features for OData (v4 and v3) Web API as summarized below:
- Referential constraint (v4, v3) #37
- Relax flag for version constraint (v4, v3): By default requests with both v3 and v4 max version headers will no longer fail. #191
- DateTime support (v4) #136
- Case-insensitive support (v4) #11
- StoreGeneratedPattern annotation (v3) #189
- ConcurrencyMode annotation and ETag (v3) #190
- Bug fixes
V4 package has a dependency on ODataLib 6.9.
Questions and feedback
You can submit questions related to this release, any issues you encounter and feature suggestions for future releases on our GitHub site.
-
7.2 OData Web API 5.4 RC
The NuGet packages for OData Web API 5.4 RC are now available on the NuGet gallery.
Download this release
You can install or update the NuGet packages for OData Web API 5.4 RC using the Package Manager Console:
PM> Install-Package Microsoft.AspNet.OData -Version 5.4.0-rc -Pre PM> Install-Package Microsoft.AspNet.WebApi.OData -Version 5.4.0-rc -Pre
What’s in this release?
This release primarily includes new features for OData (v4 and v3) Web API as summarized below:
- Referential constraint (v4, v3) #37
- Relax flag for version constraint (v4, v3): By default requests with both v3 and v4 max version headers will no longer fail. #191
- DateTime support (v4) #136
- Case-insensitive support (v4) #11
- StoreGeneratedPattern annotation (v3) #189
- ConcurrencyMode annotation and ETag (v3) #190
- Bug fixes: CodePlex & GitHub
V4 package has a dependency on ODataLib 6.9.
Questions and feedback
You can submit questions related to this release, any issues you encounter and feature suggestions for future releases on our GitHub site.
-
7.3 OData Web API 5.4
The NuGet packages for OData Web API 5.4 are now available on the NuGet gallery.
Download this release
You can install or update the NuGet packages for OData Web API 5.4 using the Package Manager Console:
What’s in this release?
This release primarily includes new features for OData (v4 and v3) Web API as summarized below:
- Referential constraint (v4, v3) #37
- Relax flag for version constraint (v4, v3): By default requests with both v3 and v4 max version headers will no longer fail. #191
- DateTime support (v4) #136
- Case-insensitive support (v4) #11
- StoreGeneratedPattern annotation (v3) #189
- ConcurrencyMode annotation and ETag (v3) #190
- Bug fixes: CodePlex & GitHub
V4 package has a dependency on ODataLib 6.9.
Questions and feedback
You can submit questions related to this release, any issues you encounter and feature suggestions for future releases on our GitHub site.
-
7.4 OData Web API 5.5
The NuGet packages for OData Web API 5.5 are now available on the NuGet gallery.
Download this release
You can install or update the NuGet packages for OData Web API 5.5 using the Package Manager Console:
What’s in this release?
This release primarily includes new features for OData v4 Web API as summarized below:
- Abstract entity type without keys or properties #182
- Complex type, Entity reference, Entity, and collection as function/action parameters #7
- Edm.Date & Edm.TimeOfDay support #156
- Open type and dynamic property on un-type scenario #171
- Nested $filter in $expand #127
- Pull requests
- Update SelectExpandQueryOption.cs #213 by Jordi Scharloo
- Add support for OpenPathSegment #222 by Brad Cleaver
- Allow multiple ODataRoutePrefix attributes #228 by Brad Cleaver
- Fix for issue #248 where a single instance function is queried using $select #249 by Hans van Bakel
- Support dynamic properties in $filter, $orderby and $select #250 by Hans van Bakel
- Bug fixes
- EnableCaseInsensitive doesn’t work right when $top #214
- DateTime doesn’t work for ETag #224
- Get parameter alias from ParameterAliasNodes by ODataLib parser #143
- DeleteRef fails if a navigation property is a derived type and there is no corresponding entity set #231
- Cannot parse Enum function parameter when its EdmType is nullable #110
- GetClrType() doesn’t work for nullable enum #241
- Enum prefix free doesn’t work for function parameter #243
- FromODataUri attribute not work for a function’s string parameter #223
- Nested $expand with $levels=max does not have correct expansion depth #148
- Enum keys not recognized by ODataConventionModelBuilder #139
In this release, we moved the license from Microsoft OpenTech + Apache to Microsoft + MIT.v4 package has a dependency on ODataLib 6.10.
Questions and feedback
You can submit questions related to this release, any issues you encounter and feature suggestions for future releases on our GitHub site.
-
7.5 OData Web API 5.5.1
The NuGet packages for OData Web API 5.5.1 are now available on the NuGet gallery.
Download this release
You can install or update the NuGet packages for OData Web API 5.5.1 using the Package Manager Console:
What’s in this release?
- Fix the issue: $count segment doesn’t work in 5.5 with EF #290
-
7.6 OData Web API 5.6 beta1
The NuGet packages for OData Web API 5.6 beta1 are now available on the NuGet gallery.
Download this release
You can install or update the NuGet packages for OData Web API 5.6 beta1 using the Package Manager Console:
PM> Install-Package Microsoft.AspNet.OData -Version 5.6-beta1 -Pre
What’s in this release?
- Convention and Attribute Routing for Dynamic Property #319
- Support the odata.include-annotations. #90
- Support UnqualifiedNameCall in attribute routing. #280
- DateTime $filter and $orderby support. #293
Bug fix
#300: Function Date() doesn’t work with Linq To Entity.
-
7.7 OData Web API 5.6
The NuGet packages for OData Web API v5.6 are available on the NuGet gallery.
Download this release
You can install or update the NuGet packages for OData Web API v5.6 using the Package Manager Console:
PM> Install-Package Microsoft.AspNet.OData -Version 5.6.0
What’s in this release?
New Features:
- GitHub Issue #71, Pull request #287 by hotchandanisagar : Add support for Delta Feed serialization in Web API OData.
- Pull request #280 by OData team : Support UnqualifiedNameCall in ODataRoute attribute.
- GitHub Issue #293, Pull request #297 by OData team : DateTime $filter and $orderby EF support.
- GitHub Issue #319 : Convention and Attribute Routing for Dynamic Property. This built upon a previous pull request #222 by Brad Cleaver: Add support for OpenPathSegment
Bug Fixes:
- GitHub Issue #300 : date() and time() function doesn’t work with EF.
- GitHub Issue #317, Pull request #340 by OData team : Support nullable referential constraint with conventional model builder.
- GitHub Issue #331, Pull request #336 by OData team : Support nullable enum prefix free in $filter.
- GitHub Issue #281, Pull request #341 by OData team : OData serialization cannot serializer the derived complex types.
- GitHub Issue #330, Pull request #350 by OData team : Dynamic property name is null use convention routing.
- GitHub Issue #214, Pull request #343 by OData team : Issue about Web API model validation.
- GitHub Issue #294, Pull request #323, #332 by OData team : Formatting
<see>true</see>
,<see langword="null"/>
issue in xml comments about true, false, and null.
OData Web API v5.6 package has a dependency on ODataLib 6.11.
Questions and feedback
You and your team are warmly welcomed to try out this new version if you are interested in the new features and fixes above. You are also welcomed to contribute your code to OData Web API repository. For any feature request, issue or idea please feel free to reach out to us at GitHub Issues.
-
7.8 OData Web API 6.0.0 alpha1
The NuGet packages for OData Web API v6.0.0 alpha1 are now available on the myget.
Configure package source
You can configure the NuGet package source for OData Web API v6.0.0 preview releases in the Package Manager Settings:
Download this release
You can install or update the NuGet packages for OData Web API v6.0.0 alpha1 using the Package Manager Console:
PM> Install-Package Microsoft.AspNet.OData -Version 6.0.0-alpha1 -Pre
What’s in this release?
This release contains the first preview of the next version of OData Web API which is built on ASP.NET 5 and MVC 6. This preview includes the basic support of:
- Querying service metadata
- Querying entity sets
- CRUD of single entity
- Querying structural or navigation property
- $filter query option
OData Web API v6.0.0 alpha1 package has a dependency on ODataLib 6.12.
Where’s the sample service?
You can take a look at a basic sample service built by this library.
Now the sample service can support (but not limit to) the following requests:
- Metadata.
GET http://localhost:9091/odata/$metadata
- EntitySet.
GET http://localhost:9091/odata/Products
- Entity.
GET http://localhost:9091/odata/Products(1)
- Structural property.
GET http://localhost:9091/odata/Customers(1)/FirstName
- Navigation property.
GET http://localhost:9091/odata/Customers(1)/Products
- $filter.
GET http://localhost:9091/odata/Products?$filter=ProductId%20gt%201
- Create.
POST http://localhost:9091/odata/Products
- Delete.
DELETE http://localhost:9091/odata/Products(2)
- Full update.
PUT http://localhost:9091/odata/Products(2)
Where’s the source code?
You can view the source code of this library at our OData Web API repository. We warmly welcome any feedback, proposition and contribution from you!
-
7.9 OData Web API 5.7 beta
The NuGet packages for OData Web API v5.7 beta are available on the NuGet gallery.
Download this release
You can install or update the NuGet packages for OData Web API v5.7 using the Package Manager Console:
PM> Install-Package Microsoft.AspNet.OData -Version 5.7.0-beta -Pre
What’s in this release?
New Features:
-
Pull request #322 by chinese007, #407 by OData team : Add the NextPageLink and Count for collection property.
-
GitHub Issue #149, Pull request #379 by OData team : Expose HasStream for EntityType configuration.
-
GitHub Issue #377, Pull request #384 by Bruce Johnston : Making ODataQueryOptions.GetNextPageLink public.
-
GitHub Issue #310, Pull request #399 by OData team : Cannot ignore some query options like $skip.
-
GitHub Issue #98, #107, #258, Pull request #405 by OData team : Add capabilities vocabulary annotation support
-
Pull request #419 by Abhishek Kumar : Adding support for alternate key Uri resolver.
-
GitHub Issue #408, Pull request #426 by OData team : Handling of preference header ‘continue-on-error’ in user request.
-
GitHub Issue #311, Pull request #428 by OData team : Auto expand navigation property.
-
GitHub Issue #304, Pull request #444 by OData team : Support setting namespaces for operations in ODataConventionModelBuilder.
Bug Fixes:
-
GitHub Issue #334, Pull request #335 by Matt Johnson : Time zone conversions are not DST aware.
-
GitHub Issue #195, Pull request #372 by Brad Cleaver : Navigation link generation for contained navigations.
-
GitHub Issue #376, Pull request #386 by OData team : $orderby with duplicate property in odata v4 failed.
-
GitHub Issue #387, Pull request #391 by OData team : Can’t get untyped enum property.
-
GitHub Issue #401, Pull request #402 by CrazyViper : DateTime min and max value serialization problem.
-
GitHub Issue #398, Pull request #430 by OData team : Change IsRelaxedMatch’s default value to True.
OData Web API v5.7-bata package has a dependency on ODataLib 6.13.
Questions and feedback
You and your team are warmly welcomed to try out this new version if you are interested in the new features and fixes above. You are also welcomed to contribute your code to OData Web API repository. For any feature request, issue or idea please feel free to reach out to us at GitHub Issues.
-
-
7.10 OData Web API 5.7 rc
The NuGet packages for OData Web API v5.7 rc are available on the NuGet gallery.
Download this release
You can install or update the NuGet packages for OData Web API v5.7 using the Package Manager Console:
PM> Install-Package Microsoft.AspNet.OData -Version 5.7.0-rc -Pre
What’s in this release?
New Features:
-
Pull request #322 by chinese007, #407 by OData team : Add the NextPageLink and Count for collection property.
-
GitHub Issue #149, Pull request #379 by OData team : Expose HasStream for EntityType configuration.
-
GitHub Issue #377, Pull request #384 by Bruce Johnston : Making ODataQueryOptions.GetNextPageLink public.
-
GitHub Issue #310, Pull request #399 by OData team : Cannot ignore some query options like $skip.
-
GitHub Issue #98, #107, #258, Pull request #405 by OData team : Add capabilities vocabulary annotation support
-
Pull request #419 by Abhishek Kumar : Adding support for alternate key Uri resolver.
-
GitHub Issue #408, Pull request #426 by OData team : Handling of preference header ‘continue-on-error’ in user request.
-
GitHub Issue #311, Pull request #428 by OData team : Auto expand navigation property.
-
GitHub Issue #304, Pull request #444 by OData team : Support setting namespaces for operations in ODataConventionModelBuilder.
Bug Fixes:
-
GitHub Issue #398, Pull request #430 by OData team : Change IsRelaxedMatch’s default value to True.
-
Pull request #407 by OData team : Add the NextPageLink & $Count for collection property
-
GitHub Issue #334, Pull request #335 by Matt Johnson : Time zone conversions are not DST aware
-
GitHub Issue #376, Pull request #386 by OData team : $orderby with duplicate property in odata v4 failed.
-
GitHub Issue #401, Pull request #402 by CrazyViper : DateTime min and max value serialization problem.
-
GitHub Issue #387, Pull request #393 by OData team : Can’t get untyped enum property.
OData Web API v5.7-rc package has a dependency on ODataLib 6.13.
Questions and feedback
You and your team are warmly welcomed to try out this new version if you are interested in the new features and fixes above. You are also welcomed to contribute your code to OData Web API repository. For any feature request, issue or idea please feel free to reach out to us at GitHub Issues.
-
-
7.11 OData Web API 5.7
The NuGet packages for OData Web API v5.7 are available on the NuGet gallery.
Download this release
You can install or update the NuGet packages for OData Web API v5.7 using the Package Manager Console:
PM> Install-Package Microsoft.AspNet.OData -Version 5.7.0
What’s in this release?
New Features:
-
Pull request #322 by chinese007, #407 by OData team : Add the NextPageLink and Count for collection property.
-
GitHub Issue #149, Pull request #379 by OData team : Expose HasStream for EntityType configuration.
-
GitHub Issue #377, Pull request #384 by Bruce Johnston : Making ODataQueryOptions.GetNextPageLink public.
-
GitHub Issue #310, Pull request #399 by OData team : Cannot ignore some query options like $skip.
-
GitHub Issue #98, #107, #258, Pull request #405 by OData team : Add capabilities vocabulary annotation support
-
Pull request #419 by Abhishek Kumar : Adding support for alternate key Uri resolver.
-
GitHub Issue #408, Pull request #426 by OData team : Handling of preference header ‘continue-on-error’ in user request.
-
GitHub Issue #311, Pull request #428 by OData team : Auto expand navigation property.
-
GitHub Issue #304, Pull request #444 by OData team : Support setting namespaces for operations in ODataConventionModelBuilder.
Bug Fixes:
-
GitHub Issue #334, Pull request #335 by Matt Johnson : Time zone conversions are not DST aware.
-
GitHub Issue #195, Pull request #372 by Brad Cleaver : Navigation link generation for contained navigations.
-
GitHub Issue #376, Pull request #386 by OData team : $orderby with duplicate property in odata v4 failed.
-
GitHub Issue #387, Pull request #391 by OData team : Can’t get untyped enum property.
-
GitHub Issue #401, Pull request #402 by CrazyViper : DateTime min and max value serialization problem.
-
GitHub Issue #398, Pull request #430 by OData team : Change IsRelaxedMatch’s default value to True.
-
GitHub Issue #442, Pull request #453 by Ilchert : Checking propetry for null changed from Or to OrElse
-
GitHub Issue #447, Pull request #457 by OData team : Keep default behavior for batch error handling.
-
GitHub Issue #459, Pull request #474 by OData team : Add concurrency annotation support.
OData Web API v5.7 package has a dependency on ODataLib 6.13.
Questions and feedback
You and your team are warmly welcomed to try out this new version if you are interested in the new features and fixes above. You are also welcomed to contribute your code to OData Web API repository. For any feature request, issue or idea please feel free to reach out to us at GitHub Issues.
-
-
7.12 OData Web API 5.8 beta
The NuGet packages for OData Web API v5.8 beta are available on the NuGet gallery.
Download this release
You can install or update the NuGet packages for OData Web API v5.8 using the Package Manager Console:
PM> Install-Package Microsoft.AspNet.OData -Version 5.8.0-beta -Pre
What’s in this release?
Improvements and fixes:
-
Fixed typographical error, changed availabe to available in README. PR #519 by orthographic-pedant
-
[ConcurrencyCheck]
attribute doesn’t work with EF. Issue #522, PR #529 -
Manually using ODataQueryOptions.Validate and setting SelectExpandQueryOption.LevelsMaxLiteralExpansionDepth. Issue #516, PR #524
-
CultureInfo property can’t be serialized. Issue #427, PR #542
-
Web API does not support Edm.Date literal in $filter when the property is Edm.Date [Nullable=True] and the backend is EF. Issue #482, PR #541
-
Add operationId for Swagger json generation. Issue #302, PR #552
-
Expand query option contain $count don’t work. Issue #349, PR #553
-
$count is evaluated prematurely at the call queryOptions.ApplyTo. Issue #1, PR #562
-
Unnecessary casts in expression when querying properties of the base class for the inheritor. Issue #560, PR #556 by Yuriy Soldatkin
OData Web API v5.8-beta package has a dependency on ODataLib 6.13.
Questions and feedback
You and your team are warmly welcomed to try out this new version if you are interested in the new features and fixes above. You are also welcomed to contribute your code to OData Web API repository. For any feature request, issue or idea please feel free to reach out to us at GitHub Issues.
-
-
7.13 OData Web API 5.8 rc
The NuGet packages for OData v4 Web API 5.8 RC are available on the NuGet gallery.
Download this release
You can install or update the NuGet packages for OData Web API v5.8 using the Package Manager Console:
PM> Install-Package Microsoft.AspNet.OData -Pre
What’s in this release?
Improvements and fixes:
-
Fixed typographical error, changed availabe to available in README. PR #519 by orthographic-pedant
-
[ConcurrencyCheck]
attribute doesn’t work with EF. Issue #522, PR #529 -
Manually using ODataQueryOptions.Validate and setting SelectExpandQueryOption.LevelsMaxLiteralExpansionDepth. Issue #516, PR #524
-
CultureInfo property can’t be serialized. Issue #427, PR #542
-
Add operationId for Swagger json generation. Issue #302, PR #552
-
$count is evaluated prematurely at the call queryOptions.ApplyTo. Issue #1, PR #562
-
Unnecessary casts in expression when querying properties of the base class for the inheritor. Issue #560, PR #556 by Yuriy Soldatkin
-
Regression about the complex inheritance build. Issue #575, PR #577
-
ProcedureConfiguration API to support types known at runtime. PR #580 by Yogev Mizrahi
-
Array of enum doesn’t work for convention model builder. Issue #581, PR #582
New Features:
-
Web API does not support Edm.Date literal in $filter when the property is Edm.Date [Nullable=True] and the backend is EF. Issue #482, PR #541
-
Expand query option contain $count don’t work. Issue #349, PR #553
-
Added UrlConventions configuration in DefaultODataPathHandler that supports ODataSimplified Uri convention. PR #599 by Gan Quan
-
Make other nested query options in to work. Issue #557, PR #569
-
Added config options SerializeNullCollectionsAsEmpty and DoNotSerializeNullCollections. Issue #490, PR #583 by nickkin-msft
-
Enable to serialize the null dynamic propery by config. Issue #313, PR #573
OData Web API v5.8 RC package has a dependency on OData v4 Lib 6.14.
Questions and feedback
You and your team are warmly welcomed to try out this new version if you are interested in the new features and fixes above. You are also welcomed to contribute your code to OData Web API repository. For any feature request, issue or idea please feel free to reach out to us at GitHub Issues.
-
-
7.14 OData Web API 5.8
The NuGet packages for OData v4 Web API 5.8 are available on the NuGet gallery.
Download this release
You can install or update the NuGet packages for OData Web API v5.8 using the Package Manager Console:
PM> Install-Package Microsoft.AspNet.OData
What’s in this release?
Improvements and fixes:
-
Fixed typographical error, changed availabe to available in README. PR #519 by orthographic-pedant
-
[ConcurrencyCheck]
attribute doesn’t work with EF. Issue #522, PR #529 -
Manually using ODataQueryOptions.Validate and setting SelectExpandQueryOption.LevelsMaxLiteralExpansionDepth. Issue #516, PR #524
-
CultureInfo property can’t be serialized. Issue #427, PR #542
-
Add operationId for Swagger json generation. Issue #302, PR #552
-
$count is evaluated prematurely at the call queryOptions.ApplyTo. Issue #1, PR #562
-
Unnecessary casts in expression when querying properties of the base class for the inheritor. Issue #560, PR #556 by Yuriy Soldatkin
-
Regression about the complex inheritance build. Issue #575, PR #577
-
ProcedureConfiguration API to support types known at runtime. PR #580 by Yogev Mizrahi
-
Array of enum doesn’t work for convention model builder. Issue #581, PR #582
New Features:
-
Web API does not support Edm.Date literal in $filter when the property is Edm.Date [Nullable=True] and the backend is EF. Issue #482, PR #541
-
Expand query option contain $count don’t work. Issue #349, PR #553
-
Added UrlConventions configuration in DefaultODataPathHandler that supports ODataSimplified Uri convention. PR #599 by Gan Quan
-
Make other nested query options in to work. Issue #557, PR #569
-
Added config options SerializeNullCollectionsAsEmpty and DoNotSerializeNullCollections. Issue #490, PR #583 by nickkin-msft
-
Enable to serialize the null dynamic propery by config. Issue #313, PR #573
OData Web API v5.8 package has a dependency on OData v4 Lib 6.14.
Questions and feedback
You and your team are warmly welcomed to try out this new version if you are interested in the new features and fixes above. You are also welcomed to contribute your code to OData Web API repository. For any feature request, issue or idea please feel free to reach out to us at GitHub Issues.
-
-
7.15 OData Web API 5.9
The NuGet packages for OData v4 Web API 5.9 are available on the NuGet gallery.
Download this release
You can install or update the NuGet packages for OData Web API v5.9 beta using the Package Manager Console:
PM> Install-Package Microsoft.AspNet.OData
What’s in this release?
Improvements and fixes:
-
Support Pass Null to EntitySet during Feed Serialization. Issue #617, PR #621
-
DataContractAttribute, etc don’t work for enum type. Issue #640
-
Provide an extensibility hook for consumers of ODataMediaTypeFormatter to customize base address of service root in OData uris. Issue #644, PR #645 by Jack Freelander
-
Using object key for null check in expression. Issue #559, PR #584 by Yuriy Soldatkin
New Features:
-
Added basic support for aggregations spec. Issue #70, PR #594 by Konstantin Kosinsky
-
Advertise action/function in feed payload. Issue #637, PR #642
OData Web API v5.9 package has a dependency on OData v4 Lib 6.15.
Questions and feedback
You and your team are warmly welcomed to try out this new version if you are interested in the new features and fixes above. You are also welcomed to contribute your code to OData Web API repository. For any feature request, issue or idea please feel free to reach out to us at GitHub Issues.
-
-
7.16 OData Web API 5.9.1
The NuGet packages for OData v4 Web API 5.9.1 are available on the NuGet gallery.
Download this release
You can install or update the NuGet packages for OData Web API v5.9.1 using the Package Manager Console:
PM> Install-Package Microsoft.AspNet.OData
What’s in this release?
Improvements and fixes:
-
POST with GeographyPoint throws object must implement IConvertable. Issue #718
-
Model binding broken with enum keys. Issue #724
-
Update enum property doesn’t work. Issue #742
-
Delta<T> should avoid static properties. Issue #137
-
MaxExpansionDepth of 0 is not work. Issue #731
-
EnumMember support without value. Issue #697
-
Make IsIfNoneMatch public. Issue #764
New Features:
- ETagMessageHandler is not supporting typeless entities. Issue #172
OData Web API v5.9.1 package has a dependency on OData v4 Lib 6.15.
Questions and feedback
You and your team are warmly welcomed to try out this new version if you are interested in the new features and fixes above. You are also welcomed to contribute your code to OData Web API repository. For any feature request, issue or idea please feel free to reach out to us at GitHub Issues.
-
-
7.17 OData Web API 6.0.0-beta
The NuGet packages for OData v4 Web API 6.0.0-beta are available on the NuGet gallery.
Download this release
You can install or update the NuGet packages for OData Web API v6.0.0-beta using the Package Manager Console:
PM> Install-Package Microsoft.AspNet.OData -Pre
What’s in this release?
Breaking Changes:
- Unify the entity and complex (collection) type serialization/deserialization, See [ odata issue #504 ]
- Rename
ODataFeed
toODataResourceSet
- Rename
ODataEntry
toODataResource
- Rename
ODataNavigationLink
toODataNestedResourceInfo
- Rename
ODataPayloadKind.Entry
toODataPayloadKind.Resource
- Rename
ODataPayloadKind.Feed
toODataPayloadKind.ResourceSet
- Rename
ODataEntityTypeSerializer
toODataResourceSerializer
- Rename
ODataFeedSerializer
toODataResourceSetSerizlier
- Rename
ODataEntityDeserializer
toODataResourceDeserializer
- Rename
ODataFeedDeserializer
toODataResourceSetDeserializer
- Remove
ODataComplexValue
- Remove
ODataComplexSerializer/ODataComplexTypeDeserializer
- Rename
- Issue #745 Support dependency injection (DI).
- Integrate with the very popular DI framework Microsoft.Extensions.DependencyInjection.
- Enable extremely easy customization of many services in Web API OData using DI.
- Simplify APIs by removing redundant parameters and properties that have corresponding services registered in DI.
- Issue #681 Using ODL path segment classes directly.
- Remove all path segment classes defined in Web API OData.
- Using the ODL path segment classes and template classes
- Issue #693 Support new model bound attributes.
- New attribute classes, ( for example
FilterAttribute
,OrderbyAttribute
, etc ) used to enhance query options validation.
- New attribute classes, ( for example
- Support complex type with navigation property.
HasMany(), HasRequired(), HasOptional
can be used on complex type to add navigation property.- Support navigation property on complex type in convention model builder.
- Remove
INavigationSourceConfiguration
andDeclaringEntityType
property
- Support multiple navigation property bindings for a single navigation property by using different paths, see [ odata issue #629 ]
- New
BindingPathConfiguration<T>
class used to add binding path - New
NavigationPropertyBindingOption
used to control binding in model builder.
- New
-
Issue #764 public
IsIfNoneMatch
property -
Issue #797 public Convert APIs in ODataModelBinderProvider.
-
Issue #172 ETagMessageHandler is not supporting typeless entities.
- Issue #652 Some changes in Delta
for complex/entity type delta.
Migration ODL changes:
-
Simplified ODL namespaces, see [ odata issue #491 ]
- Rename ODataUrlConvention to ODataUrlKeyDelimiter, see [ [odata issue #571]
(https://github.com/OData/Odata.net/issues/571) ]
- Rename ODataUrlConvention to ODataUrlKeyDelimiter.
- Use ODataUrlKeyDelimiter.Slash instead of ODataUrlConvention.Simplified or ODataUrlConvention.KeyAsSegment
- Use ODataUrlKeyDelimiter.Parentheses instead of ODataUrlConvention.Default
-
Change SerializationTypeNameAnnotation to ODataTypeAnnotation, see [ odata issue #614 ]
-
Change Enum member value type from IEdmPrimitiveValue to a more specific type, see [ odata issue #544 ]
- Adjust query node kinds in Uri Parser in order to support navigation under complex. see [ odata issue #643 ]
- Add SingleComplexNode and CollectionComplexNode to specifically represent complex type node.
- Add SingleResourceNode as the base class of SingleEntityNode and SingleComplexNode, etc.
- Rename CsdlXXX to SchemaXXX, and EdmxXXX to CsdlXXX, see [ odata issue #632 ]
- CsdlReader/Writer to SchemaReader/Writer;
- EdmxReader/Writer to CsdlReader/Writer;
- EdmxReaderSettings to CsdlReaderSettings;
- EdmxTarget to CsdlTarget
-
Remove Edm.ConcurrencyMode attribute. see [ odata issue #564 ]
- Remove
odata.null
in ODL. It’s developer’s responsibility to check whether the return object is null or not.
Improvements & Fixes:
-
Issue #719 ODataSwaggerConverter throws exception on composite key.
-
Issue #711 Using same enum twice in a model causes null reference exception.
-
Issue #697 EnumMember attribute without value.
-
Issue #726 Aggregation failed in Restier.
-
Issue #706 Change substringof to contained builtin function.
-
Issue #722 URI template parser doesn’t work correctly if key is of an enum type, see [ odata issue #556 ]
-
Issue #630 Orderby with duplicate properties.
Here can find the OData V4 7.0.0 breaking changes docs and tutorials.
Questions and feedback
You and your team are warmly welcomed to try out this new version if you are interested in the new features and fixes above. You are also welcomed to contribute your code to OData Web API repository. For any feature request, issue or idea please feel free to reach out to us at GitHub Issues.
- Unify the entity and complex (collection) type serialization/deserialization, See [ odata issue #504 ]
-
7.18 OData Web API 6.0.0
The NuGet packages for OData v4 Web API 6.0.0 are available on the NuGet gallery.
Download this release
You can install or update the NuGet packages for OData Web API v6.0.0 using the Package Manager Console:
PM> Install-Package Microsoft.AspNet.OData
What’s in this release?
Breaking Changes:
- Unify the entity and complex (collection) type serialization/deserialization, See [ odata issue #504 ]
- Rename
ODataFeed
toODataResourceSet
- Rename
ODataEntry
toODataResource
- Rename
ODataNavigationLink
toODataNestedResourceInfo
- Rename
ODataPayloadKind.Entry
toODataPayloadKind.Resource
- Rename
ODataPayloadKind.Feed
toODataPayloadKind.ResourceSet
- Rename
ODataEntityTypeSerializer
toODataResourceSerializer
- Rename
ODataFeedSerializer
toODataResourceSetSerizlier
- Rename
ODataEntityDeserializer
toODataResourceDeserializer
- Rename
ODataFeedDeserializer
toODataResourceSetDeserializer
- Remove
ODataComplexValue
- Remove
ODataComplexSerializer/ODataComplexTypeDeserializer
- Rename
- Issue #745 Support dependency injection (DI).
- Integrate with the very popular DI framework Microsoft.Extensions.DependencyInjection.
- Enable extremely easy customization of many services in Web API OData using DI.
- Simplify APIs by removing redundant parameters and properties that have corresponding services registered in DI.
- Issue #681 Using ODL path segment classes directly.
- Remove all path segment classes defined in Web API OData.
- Using the ODL path segment classes and template classes
- Issue #693 Support new model bound attributes.
- New attribute classes, ( for example
FilterAttribute
,OrderbyAttribute
, etc ) used to enhance query options validation. - Query options are disallowed by default, see detail in document.
- New attribute classes, ( for example
- Support complex type with navigation property.
HasMany(), HasRequired(), HasOptional
can be used on complex type to add navigation property.- Support navigation property on complex type in convention model builder.
- Remove
INavigationSourceConfiguration
andDeclaringEntityType
property
- Support multiple navigation property bindings for a single navigation property by using different paths, see [ odata issue #629 ]
- New
BindingPathConfiguration<T>
class used to add binding path - New
NavigationPropertyBindingOption
used to control binding in model builder.
- New
-
Issue #764 public
IsIfNoneMatch
property -
Issue #797 public Convert APIs in ODataModelBinderProvider.
-
Issue #172 ETagMessageHandler is not supporting typeless entities.
- Issue #652 Some changes in Delta
for complex/entity type delta.
Migration ODL changes:
-
Simplified ODL namespaces, see [ odata issue #491 ]
- Rename ODataUrlConvention to ODataUrlKeyDelimiter, see [ [odata issue #571]
(https://github.com/OData/Odata.net/issues/571) ]
- Rename ODataUrlConvention to ODataUrlKeyDelimiter.
- Use ODataUrlKeyDelimiter.Slash instead of ODataUrlConvention.Simplified or ODataUrlConvention.KeyAsSegment
- Use ODataUrlKeyDelimiter.Parentheses instead of ODataUrlConvention.Default
-
Change SerializationTypeNameAnnotation to ODataTypeAnnotation, see [ odata issue #614 ]
-
Change Enum member value type from IEdmPrimitiveValue to a more specific type, see [ odata issue #544 ]
- Adjust query node kinds in Uri Parser in order to support navigation under complex. see [ odata issue #643 ]
- Add SingleComplexNode and CollectionComplexNode to specifically represent complex type node.
- Add SingleResourceNode as the base class of SingleEntityNode and SingleComplexNode, etc.
- Rename CsdlXXX to SchemaXXX, and EdmxXXX to CsdlXXX, see [ odata issue #632 ]
- CsdlReader/Writer to SchemaReader/Writer;
- EdmxReader/Writer to CsdlReader/Writer;
- EdmxReaderSettings to CsdlReaderSettings;
- EdmxTarget to CsdlTarget
-
Remove Edm.ConcurrencyMode attribute. see [ odata issue #564 ]
- Remove
odata.null
in ODL. It’s developer’s responsibility to check whether the return object is null or not.
Improvements & Fixes:
-
Issue #719 ODataSwaggerConverter throws exception on composite key.
-
Issue #711 Using same enum twice in a model causes null reference exception.
-
Issue #697 EnumMember attribute without value.
-
Issue #726 Aggregation failed in Restier.
-
Issue #706 Change substringof to contained builtin function.
-
Issue #722 URI template parser doesn’t work correctly if key is of an enum type, see [ odata issue #556 ]
-
Issue #630 Orderby with duplicate properties.
-
Issue #578 The $skip and $top query options throw ODataException if overflows. Pull Request #801 by Tijmen van der Burgt.
-
Issue #418 Ignore non-odata query parameters when building parameter. Pull Request #748 by Michael Petito.
-
Issue #750 Provide a option to disable AutoExpand when $select is present.
Here can find the OData V4 7.0.0 breaking changes docs and tutorials.
Questions and feedback
You and your team are warmly welcomed to try out this new version if you are interested in the new features and fixes above. You are also welcomed to contribute your code to OData Web API repository. For any feature request, issue or idea please feel free to reach out to us at GitHub Issues.
- Unify the entity and complex (collection) type serialization/deserialization, See [ odata issue #504 ]
-
7.19 OData Web API 5.10
The NuGet packages for OData v4 Web API 5.10 are available on the NuGet gallery.
Download this release
You can install or update the NuGet packages for OData Web API v5.10 using the Package Manager Console:
PM> Install-Package Microsoft.AspNet.OData
What’s in this release?
Improvements and fixes:
-
Delta feed should only serialize changed properties. Issue #857
-
Dynamic properties set to null should be written in a Delta Feed. Issue #927
-
@odata.Etag should be written for singletons, single-valued navigation properties #926
New Features:
-
ODataProperties supports writing a delta link. Issue #900
-
A new EdmDeltaComplexObject is added to support serializing changed properties of a complex type. Issue #857
-
Added a new NavigationSource property for setting the entity set of related EdmDeltaEntityObjects and EdmDeltaDeletedEntityObjects in a flattened result. Issue #937
OData Web API v5.10 package has a dependency on OData v4 Lib 6.15.
Questions and feedback
You and your team are warmly welcomed to try out this new version if you are interested in the new features and fixes above. You are also welcomed to contribute your code to OData Web API repository. For any feature request, issue or idea please feel free to reach out to us at GitHub Issues.
-
-
7.20 OData Web API 6.1
The NuGet packages for OData v4 Web API 6.1 are available on the NuGet gallery.
What’s in this release?
Improvements and fixes:
-
Problem with ODataResourceDeserializer’s ReadInline method. Issue #1005 -
-
ResourceContext.BuildResourceInstance can null ref when model and CLR names for a property do not match. Issue #990
-
String and Byte array values not handled properly in System.Web.OData. Issue #970
-
Fix ETags on Singletons. PR #951
-
Dynamic properties set to null should be written in a Delta Feed. Issue #927
-
Delta feed should only serialize changed properties. Issue #857
-
Parse failures with special characters in untyped data. Issue #938
-
Untyped data fix. PR #936
-
Fixes for writing Delta Responses - OData 60. PR #903
-
Bug in AssociationSetDiscoveryConvention for 6.0. Issue #892
-
Complex types in delta payload seem to be broken in 6.0. Issue #891
-
$apply used with Entity Framework causes memory leaks. Issue #874
-
Create a copy constructor for SelectExpandNode. PR #870
New Features:
-
ODataProperties supports writing a delta link. Issue #900
-
A new EdmDeltaComplexObject is added to support serializing changed properties of a complex type. Issue #857
-
Added a new NavigationSource property for setting the entity set of related EdmDeltaEntityObjects and EdmDeltaDeletedEntityObjects in a flattened result. Issue #937
-
OData Web API v 5.4 does not support DateTime completely. Issue #221
-
-
7.21 OData Web API 5.11
The NuGet packages for OData v4 Web API 5.11 are available on the NuGet gallery.
What’s in this release?
Improvements and fixes:
-
Dynamic properties don’t have type. Relaxed null check in AggregateExpression and related changes in ApplyBinder to allow usage of dynamic properties in groupby clause. Pull Request #973
-
Working/fix apply enumerable.5x. Pull Request #971
-
String and Byte array values not handled properly in System.Web.OData. Issue #970
-
Fixes for writing Delta Responses - 5.x. Pull Request #901
-
Public request in ODataMediaTypeFormatter. Issue #737
-
the $skip and $top query options allow arithmetic overflows. Issue #578
-
Created virtual methods to override. Pull Request #547
-
Count with filter doesn’t work in ODATA queries. Issue #194
New Features:
- Adds support to SelectExpandBinder for etags on singletons and nav props. Pull Request #950
-
8. V1-3 SPECIFIC FEATURES
-
8.1 ConcurrencyMode and ETag
In OData V3 protocol, concurrencyMode is a special facet that can be applied to any primitive Entity Data Model (EDM) type. Possible values are
None
, which is the default, andFixed
. When used on an EntityType property,ConcurrencyMode
specifies that the value of that declared property should be used for optimistic concurrency checks. In the metadata, concurrencyMode will be shown as following:There are two approaches to set concurrencyMode for a primitive property: Using ConcurrencyCheck attribute:
Using API call
9. TEST
-
9.1 Unit Test and E2E Test
In OData WebApi, there are unit test, e2e test for V3 and V4, those test cases are to ensure the feature and bug fix, also to make sure not break old functionality.
Unit Test
Every class in OData WebApi has it’s own unit test class, for example: OData/src/System.Web.OData/OData/Builder/ActionLinkBuilder.cs ‘s test class is OData/test/UnitTest/System.Web.OData.Test/OData/Builder/ActionLinkBuilderTests.cs.
You can find that the structural under
System.Web.OData
folder andSystem.Web.OData.Test
folder are the same, also for V3System.Web.Http.OData.Test
, so if your pull request contains any class add/change, you should add/change(this change here means add test cases) unit test file.How To Add Unit Test
- Try to avoid other dependency use moq.
- Make sure you add/change the right class(V4 or V3 or both).
- Can add functinal test for complicate scenario, but E2E test cases are better.
E2E Test
E2E test are complete test for user scenarios, always begin with client request and end with server response. If your unit test in pull request can’t cover all scenario well or you have a big pull request, please add E2E test for it.
How To Add E2E Test
- Add test cases in exist test class that related to your pull request.
- Add new folder and test class for your own scenario.
- If the test has any kind of state that is preserved between request, it should be the only test defined in the test class to avoid conflicts when executed along other tests.
- Try to test with both in memory data and DB data.
- Keep test folder, class style with exist test folder, class.
Test Sample
10. OTHERS
-
10.1 How To Debug
If you want to debug OData Lib, WebAPI, Restier source, open
DEBUG
->Options and Settings
in VS, configure below things inGeneral
tab:- Uncheck
Enable Just My Code (Managed only)
. - Uncheck
Enable .NET Framework source stepping
. - One can find the source code for particular releases at https://github.com/OData/WebApi/tags. You can use these source files to properly step through your debugging session.
- Mark sure
Enable Source Link support
is checked.
Setup your symbol source in
Symbols
tab:- Check
Microsoft Symbol Servers
.- For versions of OData below 6.x, use the following
- Add location: http://srv.symbolsource.org/pdb/Public (For preview/public releases in nuget.org).
- Add location: http://srv.symbolsource.org/pdb/MyGet (For nightly build, and preview releases in myget.org).
- For versions of OData 6.x and above, use the following
- Add location: https://nuget.smbsrc.net/
- To check for the existence of the symbols for your particular version, you can run the following command using NuGet.exe:
nuget.exe list <namespace> -AllVersion -source https://nuget.smbsrc.net/
. (Example:nuget.exe list Microsoft.AspNet.OData -AllVersion -source https://nuget.smbsrc.net/
)
- For versions of OData below 6.x, use the following
- Set the cache symbols directory in your, the path should be as short as it can be.
Turn on the CLR first change exception to do a quick debug, open
DEBUG
->Exceptions
in VS, check theCommon Language Runtime Exceptions
. - Uncheck
-
10.2 Work around for SingleResult.Create an empty result
Note: This work around is for https://github.com/OData/WebApi/issues/170, which is not applicable for Microsoft.AspNetCore.OData v7.x.
When SingleResult.Create takes in a query that returns an empty result, a SerializationException is being thrown.
Let’s see a work-around about this issue.
NullEntityTypeSerializer
First of all, we define the NullEntityTypeSerializer to handle null value:
NullSerializerProvider
Now, we can define a NullSerializerProvider, we need to avoid the situation of function,action call:
Formatters
Add NullSerializerProvider in ODataMediaTypeFormatters:
11. TOOLS
-
11.1 OData V4 Web API Scaffolding
Install Visual Studio Extension
The installer of OData V4 Web API Scaffolding can be downloaded from Visual Studio Gallery: Microsoft Web API OData V4 Scaffolding. Double click vsix to install, the extension supports the VS2013 and VS2015, now.
Generate Controller Code With Scaffolding
The scaffolding is used to generate controller code for model class. Two kinds of scaffolders are provided: for model without entity framework(Microsoft OData v4 Web API Controller) and model using entity framework(Microsoft OData v4 Web API Controller Using Entity Framework).
Scaffolder for model without entity framework:
Before using scaffolding, you need to create a web api project and add model classes, the following is a sample:
Then, you can right click “Controller” folder in solution explorer, select “Add” -> “Controller”. “Microsoft OData v4 Web API Controller” will be in the scaffolder list, as following:
Select scaffoler item, then choose a model class you want to generate the controller. You can also select the “Using Async” if your data need to be got in Async call.
After click “Add”, the controller will be genereted and added into your project. Meanwhile, all reference needed, including OData Lib and OData Web API, will be added into the project, too.
Scaffolder for model using entity framework:
If want to use entity framework as provider in service, no matter whether derived class of DbContext contained in project, when right click “Controller” folder in solution explorer, select “Add” -> “Controller” -> “Microsoft OData v4 Web API Controller Using Entity Framework” as scaffolder:
Then you will see as following:
Please select the existing Model (need build before scaffolding). You can select the existing data context class or add a new one:
After click “Add”, the controller will be genereted and added into your project, new data context class will be added if needed. Meanwhile, all reference needed, including OData Lib and OData Web API, will be added into the project, too.
Change WebApiConfig.cs File
After generating the controller code, you may need to add some code in WebApiConfig.cs to generate model. Actually the code needed are in the comment of generated controller:
Just need to copy/paste the code to WebApiConfig.cs.
Add the Code to retrieve Data
As Scaffolding only genreate the frameowrk of controller code, data retrieve part should also be added into controller generated. Here, we write a simple in-memory data source and return all of them when call “GetProducts” method:
Add in ProductsController:
private static List<Product> products = new List<Product>() { new Product() {Id = 1, Name = "Test1"}, };
Add in GetProducts Method:
return Ok(products);
12. DESIGN
-
12.1 Edm.Date and Edm.TimeOfDay with EF
Problem
The Transact-SQL has date (Format: YYYY-MM-DD) type, but there isn’t a CLR type representing date type. Entity Framework (EF) only supports to use
System.DateTime
CLR type to map the date type.OData V4 lib provides a CLR
struct Date
type and the corresponding primitive type kind Edm.Date. Web API OData V4 supports it. However, EF doesn’t recognize this CLR type, and it can’t mapstruct Date
directly to date type.So, this doc describes the solution about how to support Edm.Date type with Entity Framework. Meanwhile, this doc also covers the Edm.TimeOfDay type with EF.
Scopes
It should support to map the type between date type in Database and Edm.Date type through the CLR
System.DateTime
type. The map is shown in the following figure:So, it should provide the below functionalities for the developer:
- Can configure the
System.DateTime
/System.TimeSpan
property to Edm.Date/ Edm.TimeOfDay. - Can serialize the date/ time value in the DB as Edm.Date /Edm.TimeOfDay value format.
- Can de-serialize the Edm.Date/Edm.TimeOfDay value as date/ time value into DB.
- Can do query option on the date/ time value.
Most important, EF doesn’t support the primitive collection. So, Collection of date is not in the scope. The developer can use navigation property to work around.
Detail Design
Date & Time type in SQL DB
Below is the date & time type mapping between DB and .NET:
So, From .NET view, only
System.DateTime
is used to represent the date value, meanwhile onlySystem.TimeSpan
is used to represent the time value.Date & time mapping with EF
In EF Code First, the developer can use two methodologies to map
System.DateTime
property to date column in DB:1 Data Annotation
The users can use the Column Data Annotation to modify the data type of columns. For example:
“date” is case-insensitive.
2 Fluent API
HasColumnName
is the Fluent API used to specify a column data type for a property. For example:For time type, it implicitly maps the
System.TimeSpan
to represent the time value. However, you can use string literal “time” in DataAnnotation or fluent API explicitly.CLR Date Type in ODL
OData Library defines one
struct
to hold the value of Edm.Date (Format: YYYY-MM-DD).Where, Edm.Date is the corresponding primitive type Kind.
OData Library also defines one struct to hold the value of Edm.TimeOfDay (Format: HH:MM:SS. fractionalSeconds, where fractionalSeconds =1*12DIGIT).
Where, Edm.TimeOfDay is the corresponding primitive type Kind.
Configure Date & Time in Web API by Fluent API
By default, Web API has the following mapping between CLR types and Edm types:
We should provide a methodology to map
System.DateTime
to Edm.Date type, andSystem.TimeSpan
to Edm.TimeOfDay type as follows:Extension methods
We will add the following extension methods to re-configure
System.DateTime
&System.TimeSpan
property:For example, the developer can use the above extension methods as follows:
Configure Date & Time in Web API by Data Annotation
We should recognize the Column Data annotation. So, we will add a convention class as follows:
In this class, it will identify the Column attribute applied to
System.DateTime
orSystem.TimeSpan
property, and callAsDate(…)
orAsTimeOfDay()
extension methods to add a Date or TimeOfDay mapped property. Be caution, EF supports the TypeName case-insensitive.After insert the instance of ColumnAttributeEdmPropertyConvention into the conventions in the convention model builder:
For example, the developer can do as follows to build the Edm model:
Now, the developer can call as follows to build the Edm model:
Serialize
System.DateTime
value to Edm.DateWe should modify
ODataEntityTypeSerializer
andODataComplexTypeSerializer
to identify whether or not theSystem.DataTime
is serialized to Edm.Date. So, we should add a function inODataPrimitiveSerializer
:System.TimeSpan
value to Edm.TimeOfDayAdd the following codes into the above function:
Top level property
If the end user want to query the top level property, for example:
The developer must take responsibility to convert the value into its corresponding type.
De-serialize
Edm.Date to System.DateTime value
It’s easy to add the following code in
EdmPrimitiveHelpers
to convertstruct Date
toSystem.DateTime
:Edm.TimeOfDay to System.TimeSpan value
Add codes in
EdmPrimitiveHelpers
to convertstruct TimeOfDay
toSystem.TimeSpan
:Query options on Date & Time
We should to support the following scenarios:
Fortunately, Web API supports the most scenarios already, however, we should make some codes changes in
FilterBinder
class to make TimeOfDay scenario to work.Example
We re-use the Customer model in the Scope. We use the Lambda expression to build the Edm Model as:
Here’s the metadata document:
We can query:
GET ~/Customers
We can do filter:
~/Customers?$filter=Birthday eq 2017-12-31
Thanks.
- Can configure the
-
12.2 WebApi 7.0 Default Setting Updates
WebApi Default Enable Unqualified Operations and Case-insensitive Uri
Overview
OData Layer
OData libraries 7.4.4+ contains updates to improve usability & compatibility of the library by virtue of exposing options that can be set by the caller of the OData core library (ODL). Related to request Uri parsing, the following two simplifications are now available when URI parser is configured properly:
-
As usual, namespace is the primary mechanism for resolving name token conflicts in multiple schema component of the model, therefore namespace is required up to OData v4. To improve flexibility, with notion of Default Namespaces introduced in OData v4.01, namespace qualifier is optional for function or action identifier in request Uri. When corresponding option in ODL Uri parser enabled:
-
If the function or action identifier contains a namespace qualifier, as in all the original cases, Uri parser uses original namespace-qualified semantic to ensure backward compatibility;
-
Otherwise, URI parser will search among the main schema and referenced sub-schemas treated as default namespaces, trying to resolve the unqualified function & action identifier to unique function / action element.
-
Exception will be thrown if no matches are found, or multiple functions or actions of same name are found in different namespaces of the model.
-
Property with same name as unqualified function / action name could cause the token being bound to a property segment unexpectedly. This should be avoided per best design practice in OData protocol: "Service designers should ensure uniqueness of schema children across all default namespaces, and should avoid naming bound functions, actions, or derived types with the same name as a structural or navigation property of the type."
-
-
-
In OData v4.01, case-insensitive name resolution is supported for system query options, built-in function and operator names, as well as type names, property names, enum values in form of strings. When corresponding option in ODL Uri parser is enabled, Uri parser first uses case-sensitive semantics as before and returns the result if exact match is found; otherwise, tries case-insensitive semantics, and returns the unique result found or throws exception for ambiguous result such as duplicated items.
- Most of the case-insensitive support above has been implemented in current ODL version, except for minor bug fixes and case-insensitive support for built-in function, which are addressed as part of this task.
Note the above options are also combinatorial, with expected behavior for Uri parsing.
In ODL implementation, the primary support for the two options above is the default ODataUriResolver and its derived classes.
WebApi Layer
WebApi layer utilizes dependency injection to specify various services as options for URI parser. Dependencies can be specified in WebApi layer overriding default values provided by the IServicesProvider container.
With the new default values, WebApi will exhibit different behavior related Uri parsing. The change should be backward compatible (all existing cases should work as it used to be), and previous error cases due to required case-sensitive and namespace qualifier for function should become working cases, hence improving usability of OData stack.
Scenarios
Write Scenario Description
All scenarios are related to OData Uri parsing using default WebAPI settings. All sample scenarios assume no name collisions in EDM model, unless noted otherwise.
Functions: namespace qualified/unqualified
With function defined in the model: builder.EntityType<Customer>().Action("UpdateAddress");
- Namespace qualified function should work as before:
POST /service/Customers(1)/Default.UpdateAddress()
- Namespace unqualified function should become a successful case:
POST /service/Customers(1)/UpdateAddress()
Case-Insensitive name resolution:
-
Case-insensitive property name should become resolvable:
With model: public class InvalidQueryCustomer { public int Id { get; set; } }
GET /service/InvalidQueryCustomers?$filter=id eq 5 : HTTP 200
GET /service/InvalidQueryCustomers(5)?$filter=id eq 5 : HTTP 400 “Query options $filter, $orderby, $count, $skip, and $top can be applied only on collections.”
-
Case-insensitive customer uri function name should become resolvable:
With model having entity type People defined and following customized Uri function:
FunctionSignatureWithReturnType myFunc
= new FunctionSignatureWithReturnType(EdmCoreModel.Instance.GetBoolean(true),
EdmCoreModel.Instance.GetString(true), EdmCoreModel.Instance.GetString(true));
// Add a custom uri function
CustomUriFunctions.AddCustomUriFunction("myMixedCasestringfunction", myFunc);
This should work:
GET /service/People?$filter=mYMixedCasesTrInGfUnCtIoN(Name,'BlaBla') : HTTP 200
-
Combination of case-insensitive type & property name and unqualified function should become resolvable:
With controller:
[HttpGet]
public ITestActionResult CalculateTotalOrders(int key, int month) {/*…*/}
Following OData v4 Uris should work:
GET /service/Customers(1)/Default. CalculateTotalOrders (month=1) : HTTP 200 GET /service/CuStOmErS(1)/CaLcUlAtEToTaLoRdErS (MONTH=1) : HTTP 200
Design Strategy
Dependency Injection of ODataUriResolver
ODL (Microsoft.OData.Core) library supports dependency injection of a collection of service types from client via the IServiceProvider interface. The IServiceProvider can be considered as a container populated with default objects by ODL, while ODL’s client, such as WebApi, can override default objects by injecting customized dependencies.
ODL IContainerBuilder and ContainerBuilderExtensions
The ContainerBuilderExtensions.AddDefaultODataServices(this IContainerBuilder) implementation populates a collection of default OData service objects into the container’s builder. For example, default service of type ODataUriResolver is registered as one instance of ODataUriResolver as follows:
public static IContainerBuilder AddDefaultODataServices(this IContainerBuilder builder)
{
//………
builder.AddService(ServiceLifetime.Singleton,
sp => ODataUriResolver.GetUriResolver(null));
//………
}
WebAPI dependency injection of customized ODataUriResolver:
-
WebAPI defines the DefaultContainerBuilder implementing the ODL’s IContainerBuilder interface.
-
When root container is created from HttpConfiguration (via the HttpConfigurationExtensions.CreateODataRootContainer), a PerRouteContainer instance will be used to:
-
Create an instance of DefaultContainerBuilder populated with default OData services noted in above;
-
Override the ODL’s default ODataUriResolver service instance in the container builder with WebApi’s new default for UnqualifiedODataUriResover with EnableCaseInsensitive=true.
protected IContainerBuilder CreateContainerBuilderWithCoreServices()
{
//......
builder.AddDefaultODataServices();
// Set Uri resolver to by default enabling unqualified functions/actions and case insensitive match.
builder.AddService(
ServiceLifetime.Singleton,
typeof(ODataUriResolver),
sp => new UnqualifiedODataUriResolver {EnableCaseInsensitive = true});
return builder;
}
-
WebAPI client (per service) can further inject other dependencies (for example, typically, adding the EDM model) through the’configureAction’ argument of the following method from HttpConfigurationExtensions:
internal static IServiceProvider CreateODataRootContainer(this HttpConfiguration configuration, string routeName, Action<IContainerBuilder> configureAction)
-
ODataUriParser configuration with injected ODataUriResolver dependency
When WebApi parses the request Uri, instance of ODataDefaultPathHandler is created with associated service provider container, which is further used to create ODataUriParser with injected dependency of ODataUriResolver.
public ODataUriParser(IEdmModel model, Uri relativeUri, IServiceProvider container)
Enable Case-Insensitive for Custom Uri function
One issue is encountered when trying to bind function call token with case-insensitive enabled. The reason is that at the very beginning of the function BindAsUriFunction() the name token, when case-insensitive is enabled, is coerced to lower case (as shown below), which is valid for build-in function (such as ‘startswith’ and ‘geo.distance’, etc), but might not be valid for custom uri functions.
private QueryNode BindAsUriFunction(FunctionCallToken functionCallToken, List<QueryNode> argumentNodes)
{
if (functionCallToken.Source != null)
{
// the parent must be null for a Uri function.
throw new ODataException(ODataErrorStrings.FunctionCallBinder_UriFunctionMustHaveHaveNullParent(functionCallToken.Name));
}
string functionCallTokenName = this.state.Configuration.EnableCaseInsensitiveUriFunctionIdentifier ? functionCallToken.Name.ToLowerInvariant() : functionCallToken.Name;
To implement with the correct behavior for enabled case-insensitive:
-
GetUriFunctionSignatures needs to additionally return signatures associated with function names in a dictionary instance.
-
When resolving best match function based on arguments for the invoked function, MatchSignatureToUriFunction will find the best match. Exception is still thrown in case of ambiguity or no matches found.
Work Items
-
-
12.3 Use $skiptoken for server side paging
Use $skiptoken for server-driven paging
Background
Loading large data can be slow. Services often rely on pagination to load the data incrementally to improve the response times and the user experience. Paging can be server-driven or client-driven:
Client-driven paging
In client-driven paging, the client decides how many records it wants to load and asks the server that many records. That is achieved by using $skip and $top query options in conjunction. For instance, if a client needs to request 10 records from 71-80, it can send a similar request as below:
GET ~/Products/$skip=70&$top=10
Server-driven paging
In server-driven paging, the client asks for a collection of entities and the server sends back partial results as well as a nextlink to use to retrieve more results. The nextlink is an opaque link which may use $skiptoken to store state about the request, such as the last read entity.
Problem
Currently, WebAPI uses $skip for server-driven paging which is a slight deviation from the OData standard and can be problematic when the data source can get updated concurrently. For instance, a deletion of a record may cause the last record to be sent down to the client twice.
Proposed Solution
WebAPI will now implement $skiptoken. When a collection of entity is requested which requires paging, we will assign the key value of the last sent entity to $skiptoken in the nextlink url prepended by values of the orderby properties in same order. While processing a request with $skiptoken, we will add another condition to the predicate based on the value of the skipoken.
Technical details
After all the query options have been applied, we determine if the results need pagination. If the results need pagination, we will pass the generated skiptoken value based off of the last result to the method that generates the nextpage link.
Format of the nextlink
The nextlink may contain $skiptoken if the result needs to be paginated. In WebAPI the $skiptoken value will be a list of pairs, where the pair consists of a property name and property value separated by a delimiter(:). The orderby property and value pairs will be followed by key property and value pairs in the value for $skiptoken. Each property and value pair will be comma separated.
~/Products?$skiptoken=Id:27 ~/Books?$skiptoken=ISBN:978-2-121-87758-1,CopyNumber:11 ~/Products?$skiptoken=Id:25&$top=40 ~/Products?$orderby=Name&$skiptoken=Name:'KitKat',Id:25&$top=40 ~/Cars(id)/Colors?$skip=4
We will not use $skiptoken if the requested resource is not an entity type. Rather, normal skip will be used.
This is the default format but services can define their own format for the $skiptoken as well but in that case, they will have to parse and generate the skiptoken value themselves.
Generating the nextlink
The next link generation method in GetNextPageHelper static class will take in the $skiptoken value along with other query parameters and generate the link by doing special handling for $skip, $skiptoken and $top. It will pass on the other query options as they were in the original request.
1. Handle $skip
We will omit the $skip value if the service is configured to support $skiptoken and a collection of entity is being requested. This is because the first response would have applied the $skip query option to the results already.
2. Handle $top
The next link will only appear if the page size is less than the $top query option. We will reduce the value of $top query option by the page size while generating the next link.
3. Handle $skiptoken
The value for the $skiptoken will be updated to new value passed in which is the key value for the last record sent. If the skiptoken value is not sent, we will call the existing method and use $skip for paging instead.
Routing
Since we will only be modifying the query options from the original request to generate the nextlink, the routing will remain same as the original request.
Parsing $skiptoken and generating the Linq expression
New classes will be created for SkipTokenQueryOption and SkipTokenQueryValidator. SkipTokenQueryOption will contain the methods to create and apply the LINQ expression based on the $skiptoken value. To give an example, for a query like the following:
GET ~/EntitySet?$orderby=Prop1,Prop2&$skiptoken=Prop1:value1,Prop2:value2,Id1:idVal1,Id2:idVal2
The following where clause will be added to the predicate:
WHERE Prop1>value1 Or (Prop1=value1 AND Prop2>value2) Or (Prop1=value1 AND Prop2=value2 AND Id1>Val) Or (Prop1=value1 AND Prop2=value2 AND Id1=idVal1 AND Id2>idVal2)
Note that the greater than operator will be swapped for less than operator if the order is descending.
Generating the $skiptoken
The SkipTokenQueryOption class will be utilized by ODataQueryOption to pass the token value to the nextlink generator helper methods. In the process, IWebApiRequestMessage will be modified and GetNextPageLink method will be overloaded to now accept another parameter for the $skiptoken value.
Configuration to use $skiptoken or $skip for server-driven paging
We will allow services to configure if they want to use $skiptoken or $skip for paging per route as there can be performance issues with a large database with multipart keys. By default, we will use $skip.
Moreover, we will ensure stable sorting if the query is configured for using $skiptoken.
Additional details and discussions
1. How would a developer implement paging without using EnableQuery attribute? What about stable ordering in that case?
a. The new SkipTokenQueryOption class will provide 2 methods-
i. GenerateSkipTokenValue – requires the EDM model, the results as IQuerable and OrderbyQueryOption. ii. ApplyTo - applies the LINQ expression for $skiptoken. iii. ParseSkipTokenValue - Populates the dictionary of property-value pairs on the class
For developers having non-linq data sources, they can generate the skiptoken value using the new class and use this class in their own implementation of the filtering that ApplyTo does.
b. To ensure stable ordering, we will provide a public method on ODataQueryOptions - GenerateStableOrderQueryOption: It will output an OrderbyQueryOption which can be passed to the skiptoken generator.
Developers not using the EnableQuery attribute will have to generate their own OrderbyQueryOption and generate the skiptoken value themselves.
2. Should the nextlink modify the list of orderby properties to ensure stable ordering?
Currently, the way the code is structured, a lot of the information about the current query ($apply and $orderby) would need to be passed down to the nextlink generator to append to the orderby and moreover, it will make it very cumbersome for developers not using the enable query attribute to use it.
Instead, we will expose methods on ODataQueryOption that will enable developers to generate their orderby clauses for stable sorting.
3. Parameterizing the nextlink instead of using skiptoken?
Currently, the developers not using the enable query attribute generate the next link by using GetNextPageLink extension method on the request. Considering that the data source can even be linq incompatible, this will be a significant deviation from the current implementation for such developers. Moreover, the need to filter the results based on a certain value fits more into the QueryOption paradigm and makes it more suitable for customers supporting linq.
13. 6.X FEATURES
-
13.1 Model Bound Attributes
Since Web API OData V6.0.0 which depends on OData Lib 7.0.0, we add a new feature named ModelBoundAttribute, use this feature, we can control the query setting through those attributes to make our service more secure and even control the query result by set page size, automatic select, automatic expand.
Let’s see how to use this feature.
Global Query Setting
Now the default setting for WebAPI OData is : client can’t apply
$count
,$orderby
,$select
,$top
,$expand
,$filter
in the query, query likelocalhost\odata\Customers?$orderby=Name
will failed as BadRequest, because all properties are not sort-able by default, this is a breaking change in 6.0.0, if we want to use the default behavior that all query option are enabled in 5.x version, we can configure the HttpConfigration to enable the query option we want like this:Page Attribute
Pagination settings correlate to OData’s @odata.nextLink (server-side pagination) and
?$top=5&$skip=5
(client-side pagination). We can set the PageSize to control the server-side pagination, and MaxTop to control the maximum value for$top
, by default client can’t use$top
as we said in theGlobal Query Setting
section, every query option is disabled, if you set thePage
Attribute, by default it will enable the$top
with no-limit maximum value, or you can set the MaxTop like:In the model above, we defined the page setting for Customer and Orders navigation property in Customer and Customers navigation property in Order, let’s explain the usage of them one by one.
Page Attribute on Entity Type
The first page attribute on Customer type, means the query setting when we query the Customer type, like
localhost\odata\Customers
, the max value for$top
is 5 and page size of returned customers is 1.For example:
The query like
localhost\odata\Customers?$top=10
will failed with BadRequest : The limit of ‘5’ for Top query has been exceeded.The page size is 1 if you request
localhost\odata\Customers
.Page Attribute on Navigation Property
And what about the page attribute in Order type’s navigation property Customers? it means the query setting when we query the Customers navigation property in Order type. Now we get a query setting for Customer type and a query setting for Customers navigation property in Order type, how do we merge these two settings? The answer is: currently the property’s query setting always override the type’s query setting, if there is no query setting on property, it will inherent query setting from it’s type.
For example:
The query like
localhost\odata\Orders?$expand=Customers($top=10)
will works because the setting is no limit.The result of
localhost\odata\Orders?$expand=Customers
won’t have paging because the setting didn’t set the page size.So for the attribute on Orders navigation property in Customer type, the page size and maximum value of
$top
setting will have effect when we request likelocalhost\odata\Customers?$expand=Orders
orlocalhost\odata\Customers(1)\Orders
as long as we are query the Orders property on Customer type.Count Attribute
Count settings correlate to OData’s
?$count=true
(items + count). We can set the entity type or property is countable or not like:In the model above, we can tell that the Order is not countable(maybe the number is very large) but Orders property in Customer is countable.
About the priority of attribute on property and type, please refer to
Page Attribute
section.So you can have those examples:
Query
localhost\odata\Orders?$count=true
will failed with BadRequest that orders can’t used for$count
Query
localhost\odata\Customers?$expand=Orders($count=true)
will worksQuery
localhost\odata\Customers(1)/Orders?$count=true
works too.OrderBy Attribute
Ordering settings correlate to OData’s
$orderby
query option. We can specify which property is sort-able very easy and we can also define very complex rule by use attribute on property and on type. For example:Multiple Attribute
We can see that the we can have multiple OrderBy attributes, how are they merged? The answer is the Attribute on a class with a constrained set of properties gets high priority, the order of their appear time doesn’t matter.
OrderBy Attribute on EntityType and ComplexType
Let’s go through those attributes to understand the settings, the first attribute means we can specify the single navigation property
AutoExpandOrder
and single complex propertyAddress
when we query Customer type, like querylocalhost\odata\Customers?$orderby=Address/xxx
orlocalhost\odata\Customers?$orderby=AutoExpandOrder/xxx
. And how do we control which property under AutoExandOrder is sort-able?For the AutoExpandOrder property, we add OrderBy Attribute on Order type, the first attribute means
Name
is not sort-able, the second attribute means all the property is sort-able, so for the Order type, properties exceptName
are sort-able.For example:
Query
localhost\odata\Customers?$orderby=Name
will failed with BadRequest that Name is not sort-able.Query
localhost\odata\Customers?$orderby=AutoExpandOrder/Price
works.Query
localhost\odata\Customers?$orderby=AutoExpandOrder/Name
will failed with BadRequest that Name is not sort-able.OrderBy Attribute on Property
About the priority of attribute on property and type, please refer to
Page Attribute
section. We have OrderBy attribute on Address property, it means all properties are sort-able when we query Customer, and for Orders property, it means onlyId
is sort-able when we query Orders property under Customer.For example:
Query
localhost\odata\Customers?$orderby=Address/Name
works.Query
localhost\odata\Customers?$expand=Orders($orderby=Id)
works.Query
localhost\odata\Customers?$expand=Orders($orderby=Price)
will failed with BadRequest that Price is not sort-able.Filter Attribute
Filtering settings correlate to OData’s
$filter
query option. For now we only support to specify which property can be filter just like what we do in OrderBy Attribute, we can simply replace orderby with filter in the example above, so please refer toOrderBy Attribute
section.Select Attribute
Search settings correlate to OData’s
$search
query option. We can specify which property can be selected, which property is automatic selected when there is no$select
in the query.Automatic Select
Automatic select mean we will add
$select
in the query depends on the select attribute.If we have a User class, and we don’t want to expose some property to client, like secrete property, so client query
localhost\odata\Users?$select=Secrete
will failed and querylocalhost\odata\Users?
won’t return Secrete property, how can we achieve that with Select Attribute?The first attribute means all the property will be automatic select when there is no
$select
in the query, the second attribute means the property Secrete is not select-able. For example, requestlocalhost\odata\Users
will have the same response withlocalhost\odata\Users?$select=Id,Name
Automatic Select on Derived Type
If the target type of our request have some derived types which have automatic select property, then these property will show in the response if there is no
$select
query option, for example, requestlocalhost\odata\Users
will have the same response withlocalhost\odata\Users?$select=Id,Name,SpecialUser/SpecialName
if the SpecinalName property in automatic select.Select Attribute on Navigation Property
About the priority of attribute on property and type, please refer to
Page Attribute
section. About the multiple attribute, please refer toMultiple Attribute
section. We also support Select attribute on navigation property, to control the expand scenario and property access scenario, like if we want client can only selectId
andName
from Customer’s navigation property Order.Expand Attribute
Expansion settings correlate to OData’s
$expand
query option. We can specify which property can be expanded, which property is automatic expanded and we can specify the max depth of the expand property. Currently we support Expand attribute on entity type and navigation property, the using scenario is quite like Select Attribute and other attributes, you can just refer to those sections.Automatic Expand
Automatic expand mean it will always expand that navigation property, it’s like automatic select, we will add a $expand in the query, so it will expand even if there is a
$select
which does not contain automatic epxand property.Model Bound Fluent APIs
We also provide all fluent APIs to configure above attributes if you can’t modify the class by adding attributes, it’s very straight forward and simple to use:
The example shows class with attributes and build model using the model bound fluent APIs if we can’t modify the class. These two approaches are getting two same models. About the multiple attribute, model bound fluent APIs are the same, the model bound fluent API with a constrained set of properties wins. For example:
builder.EntityType<Customer>().Expand().Expand("Friend", SelectExpandType.Disabled)
, Friend can’t be expanded, even we putExpand()
in the end. If there is a setting with same property, the last one wins, for example:.Expand(8, "Friend").Expand(1, "Friend")
, the max depth will be 1.Overall Query Setting Precedence
Query settings can be placed in many places, with the following precedence from lowest to highest: System Default(not query-able by default), Global Configuration, Model Bound Attribute, Fluent API.
Controller Level Query Setting
If we only want to control the setting in one API call, like the
Get()
method inCustomerController
, we can simply use the Settings in EnableQueryAttribute, like:The model bound attribute and the settings in EnableQueryAttribute are working separately, so the query violent one of them will fail the request.
More test case with more complex scenario can be find at here.
You can participate into discussions and ask questions about this feature at our GitHub issues, your feedback is very important for us.
-
13.2 Complex Type with Navigation Property
Since Web API OData V6.0.0 beta, It supports to configure navigation property on complex type.
Let’s have an example to illustrate how to configure navigation property on complex type:
CLR Model
We use the following CLR classes as the CLR model:
Where:
- Address is a complex type.
- City is an entity type.
Add navigation properties
The following APIs are used to add navigation properties for complex type:
So, we can do as:
We can get the following result:
<ComplexType Name="Address"> <NavigationProperty Name="CityInfo" Type="ModelLibrary.City" Nullable="false" />" <NavigationProperty Name="Cities" Type="Collection(ModelLibrary.City)" />" </ComplexType> <EntityType Name="City"> <Key> <PropertyRef Name="Id" /> </Key> <Property Name="Id" Type="Edm.Int32" Nullable="false" /> </EntityType>
Add navigation properties in convention model builder
Convention model builder will automatically map the class type properties in complex type as navigation properties if the declaring type of such navigation property has key defined.
So, as the above example, we can use the following codes to define a convention model:
As result, We can get the following result:
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"> <edmx:DataServices> <Schema Namespace="ModelLibrary" xmlns="http://docs.oasis-open.org/odata/ns/edm"> <ComplexType Name="Address"> <NavigationProperty Name="CityInfo" Type="ModelLibrary.City" /> <NavigationProperty Name="Cities" Type="Collection(ModelLibrary.City)" /> </ComplexType> <EntityType Name="City"> <Key> <PropertyRef Name="Id" /> </Key> <Property Name="Id" Type="Edm.Int32" Nullable="false" /> </EntityType> </Schema> <Schema Namespace="Default" xmlns="http://docs.oasis-open.org/odata/ns/edm"> <EntityContainer Name="Container" /> </Schema> </edmx:DataServices> </edmx:Edmx>
-
13.3 Multiple Navigation property binding path
Since Web API OData V6.0.0 beta, It supports to configure multiple navigation property binding paths.
Original navigation property binding
The following APIs are used to add navigation property binding in model builder.
1. HasManyBinding() 2. HasRequiredBinding() 3. HasOptionalBinding()
So, we can do as:
We can get the following result:
<EntityContainer Name="Container"> <EntitySet Name="Customers" EntityType="System.Web.OData.Builder.Cusomter"> <NavigationPropertyBinding Path="Orders" Target="Orders" /> <NavigationPropertyBinding Path="SingleOrder" Target="SingleOrders" /> </EntitySet> <EntitySet Name="Orders" EntityType="System.Web.OData.Builder.Order" /> <EntitySet Name="SingleOrders" EntityType="System.Web.OData.Builder.Order" /> </EntityContainer>
Where, the binding path is straight-forward, just as the navigation property name. Before 6.0.0, it doesn’t support to:
- Add the complex property into binding path
- Add the type cast into binding path
Multiple navigation property binding path in model builder
In Web API OData V6.0.0 beta, we add a new generic type class to configure the multiple binding path:
In this class, it provides the following APIs to add binding path:
It also provides the following APIs to bind the navigation property with multiple binding path to a targe navigation source.
So, the normal navigation property binding configuration flow is:
- Call
Binding
fromNavigationSourceConfiguration{TEntityType}
to get an instance of BindingPathConfiguration{TEntityType} - Call
HasManyPath()/HasSinglePath
from BindingPathConfiguration{TEntityType}` to add a binding path - Repeat step-2 if necessary
- Call
HasManyBinding()/HasRequiredBinding()/HasOptionalBinding()
to add the target navigation source.
Here’s an example:
Example
Let’s have the following CLR classes as the model:
Entity types
Complex types
Add navigation property binding:
So, we can get the following target binding:
<Schema Namespace="Default" xmlns="http://docs.oasis-open.org/odata/ns/edm"> <EntityContainer Name="Container"> <EntitySet Name="Customers" EntityType="System.Web.OData.Builder.Cusomter"> <NavigationPropertyBinding Path="Pet/System.Web.OData.Builder.Human/HumanAddress/SubCity" Target="HumanCities" /> <NavigationPropertyBinding Path="Pet/System.Web.OData.Builder.Horse/HorseAddress/SubCity" Target="HorseCities" /> <NavigationPropertyBinding Path="Pet/System.Web.OData.Builder.Horse/HorseAddresses/SubCity" Target="HorseCities" /> <NavigationPropertyBinding Path="System.Web.OData.Builder.VipCustomer/VipLocations/System.Web.OData.Builder.UsAddress/SubCity" Target="B" /> <NavigationPropertyBinding Path="System.Web.OData.Builder.VipCustomer/VipLocations/City" Target="A" /> </EntitySet> <EntitySet Name="A" EntityType="System.Web.OData.Builder.City" /> <EntitySet Name="B" EntityType="System.Web.OData.Builder.City" /> <EntitySet Name="HumanCities" EntityType="System.Web.OData.Builder.City" /> <EntitySet Name="HorseCities" EntityType="System.Web.OData.Builder.City" /> </EntityContainer> </Schema>
Where: As example shown:
- It supports single property with multiple binding path.
- It supports collection property with mutliple binding path.
- It also supports mulitple binding path with inheritance type.
Besides, the
HasManyPath()/HasSinglePath()
has onverload methods to configure the containment binding path.Multiple navigation property binding path in convention model builder
In convention model builder, it will automatically traverl all property binding paths to add a binding for the navigation proeprty.
There’s an unit test that you can refer to.
-
13.4 Dependency Injection Support
Since Web API OData V6.0.0 beta, we have integrated with the popular dependency injection (DI) framework Microsoft.Extensions.DependencyInjection. By means of DI, we can significantly improve the extensibility of Web API OData as well as simplify the APIs exposed to the developers. Meanwhile, we have incorporated DI support throughout the whole OData stack (including ODataLib, Web API OData and RESTier) thus the three layers can consistently share services and custom implementations via the unified DI container in an OData service. For example, if you register an
ODataPayloadValueConverter
in a RESTier API class, the low-level ODataLib will be aware of that and use it automatically because they share the same DI container.For the fundamentals of DI support in OData stacks, please refer to this docs from ODataLib. After understanding that, we can now take a look at how Web API OData implements the container, takes use of it and injects it into ODataLib.
Implement the Container Builder
By default, if you don’t provide a custom container builder, Web API OData will use the
DefaultContainerBuilder
which implementsIContainerBuilder
from ODataLib. The default implementation is based on the Microsoft DI framework introduced above and what it does is just delegating the builder operations to the underlyingServiceCollection
.But if you want to use a different DI framework (e.g., Autofac) or make some customizations to the default behavior, you will need to either implement your own container builder from
IContainerBuilder
or inherit from theDefaultContainerBuilder
. For the former one, please refer to the docs from ODataLib. For the latter one, here is a simple example to illustrate how to customize the default container builder.After implementing the container builder, you need to register that container builder in
HttpConfiguration
to tell Web API OData that you want to use your custom one. Please note that you MUST callUseCustomContainerBuilder
BEFOREMapODataServiceRoute
andEnableDependencyInjection
because the root container will be actually created in these two methods. Setting the container builder factory after its creation is meaningless. Of course, if you wish to keep the default container builder implementation,UseCustomContainerBuilder
doesn’t need to be called at all.Register the Required Services
Basic APIs to register the services have already been documented here. Here we mainly focus on the APIs from Web API OData that help to register the services into the container builder. The key API to register the required services for an OData service is an overload of
MapODataServiceRoute
which takes aconfigureAction
to configure the container builder (i.e., register the services).Theoretically you can register any service within the
configureAction
but there are two mandatory services that you are required to register: theIEdmModel
and a collection ofIRoutingConvention
. Without them, the OData service you build will NOT work correctly. Here is an example of calling the API where a custom batch handlerMyBatchHandler
is registered. You are free to register any other service you like to thebuilder
.You might also find that we still preserve the previous overloads of
MapODataServiceRoute
which take batch handlers, path handlers, HTTP message handlers, etc. They are basically wrapping the first overload that takes aconfigureAction
. The reason why we keep them is that we want to give the users convenience to create OData services and bearings to the APIs they are familiar with.Once you have called any of the
MapODataServiceRoute
overloads, the dependency injection for that OData route is enabled and an associated root container is created. As we internally maintain a dictionary to map the route name to its corresponding root container (1-1 mapping), multiple OData routes (i.e., callingMapODataServiceRoute
multiple times) are still working great and the services registered in different containers (or routes) will not impact each other. That said, if you want a custom batch handler to work in the two OData routes, register them twice.Enable Dependency Injection for HTTP Routes
It’s also possible that you don’t want to create OData routes but just HTTP routes. The dependency injection support will NOT be enabled right after you call
MapHttpRoute
. In this case, you have to callEnableDependencyInjection
to enable the dependency injection support for ALL HTTP routes. Please note that all the HTTP routes share the SAME root container which is of course different from the one of any OData route. That said callingEnableDependencyInjection
has nothing to do withMapODataServiceRoute
.Please also note that the order of
MapHttpRoute
andEnableDependencyInjection
doesn’t matter because they have no dependency on each other.Manage and Access the Request Container
Given a root container, we can create scoped containers from it, which is also known as request containers. Mostly you don’t need to manage the creation and destruction of request containers yourself but there are some rare cases you have to touch them. Say you want to implement your custom batch handler, you have the full control of the multi-part batch request. You parse and split it into several batch parts (or sub requests) then you will be responsible for creating and destroying the request containers for the parts. They are implemented as extension methods to
HttpRequestMessage
inHttpRequestMessageExtensions
.To create the request container, you need to call the following extension method on a request. If you are creating the request container for a request that comes from an HTTP route, just pass
null
for therouteName
.To delete the request container from a request, you need to call the following extension method on a request. The parameter
dispose
indicates whether to dispose that request container after deleting it from the request. Disposing a request container means that all the scoped and transient services within that container will also be disposed if they implementIDisposable
.To get the request container associated with that request, simply call the following extension method on a request. Note that you don’t need to provide the route name to get the request container because the container itself has already been stored in the request properties during
CreateRequestContainer
. There is also a little trick inGetRequestContainer
that if you have never calledCreateRequestContainer
on the request but directly callGetRequestContainer
, it will try to create the request container for all the HTTP routes and return that container. Thus the return value ofGetRequestContainer
should never benull
.Please DO pay attention to the lifetime of the services. DON’T forget to delete and dispose the request container if you create it yourself. And scoped services will be disposed after the request completes.
Services Available in Web API OData
Currently services Available in Web API OData include:
IODataPathHandler
whose default implementation isDefaultODataPathHandler
and lifetime isSingleton
.XXXQueryValidator
whose lifetime are allSingleton
.ODataXXXSerializer
andODataXXXDeserializer
whose lifetime are allSingleton
. But please note that they are ONLY effective whenDefaultODataSerializerProvider
andDefaultODataDeserializerProvider
are present. Custom serializer and deserializer providers are NOT guaranteed to call those serializers and deserializers from the DI container.ODataSerializerProvider
andODataDeserializerProvider
whose implementation types areDefaultODataSerializerProvider
andDefaultODataDeserializerProvider
respectively and lifetime are allSingleton
. Please note that you might lose all the default serializers and deserializers registered in the DI container if you don’t call into the default providers in your own providers.IAssembliesResolver
whose implementation type is the default one from ASP.NET Web API.FilterBinder
whose implementation type isTransient
because eachEnableQueryAttribute
instance will create its ownFilterBinder
. Override it if you want to customize the process of binding a $filter syntax tree.
Services Avaliable in OData Lib
Services in OData Lib also can be injected through Web API OData.
-
13.5 Use ODL path classes
Since Web API OData V6.0.0 beta, Web API OData uses the ODL path classes directly.
In Web API OData V5.x, there are a lot of path segment classes defined to wrapper the corresponding path segment classes defined in ODL. for example:
Where,
- Some segments are just used to wrapper the corresponding segments defined in ODL, such as BatchPathSegment.
- Some segments are used to do some conversions, such as BoundFunctionPathSegment, KeyValuePathSegment.
However, ODL defines the same segments and Web API OData needn’t to do such conversion. So, all segment classes defined in Web API OData are removed in v6.x.
Web API OData will directly use the path segment classes defined in ODL as follows:
-
13.6 Key value binding
Since Web API OData V6.0.0 beta, Web API OData supports the composite key convention binding.
Let’s have an example:
Where, Customer is an entity type with three properties. We will make all these trhee properties as the composite keys for Customer entity type.
So, we can do:
Before Web API OData V6.x, key segment convention routing only supports the single key convention binding, just use the key as the parameter name.
In Web API OData V6.x, we use the following convention for the composite key parameter name, but leave the key for single key parameter.
Therefore, for StringProp key property, the action parameter name should be keyStringProp.
Let’s see how the contoller action looks like:
Now, you can issue a request:
The result is:
Be noted, this rule also applys to the navigation property key convention binding.
-
13.7 Merge complex & entity value serialize/deserialize
You can refer the detail in: complex type and entity type in ODL 7.0 doc.
In Web API OData 6.x, we do the following to support the merge of complex type and entity type:
- Rename ODataFeed to ODataResourceSet
- Rename ODataEntry to ODataResource
- Rename ODataNavigationLink to ODataNestedResourceInfo
- Rename ODataPayloadKind.Entry to ODataPayloadKind.Resource
- Rename ODataPayloadKind.Feed to ODataPayloadKind.ResourceSet
- Rename ODataEntityTypeSerializer to ODataResourceSerializer
- Rename ODataFeedSerializer to ODataResourceSetSerizlier
- Rename ODataEntityDeserializer to ODataResourceDeserializer
- Rename ODataFeedDeserializer to ODataResourceSetDeserializer
- Remove ODataComplexValue
- Remove ODataComplexSerializer/ODataComplexTypeDeserializer
So, for any complex value, it will use ODataResourceSerializer and ODataResourceDeserializer to read and write. for any complex collection value, it will use ODataResourceSetSerializer and ODataResourceSetDeserializer to read and write.
14. 7.X FEATURES
-
14.1 7.0 Beta1 & Beta2 & Beta4
Web API OData Beta1, Beta2 & Beta4 includes a new package for WebApi OData V7.0.0 for ASP.NET Core 2.x. The nightly version of this package is available from this nightly url.
There is also a new WebApi OData V7.0.0 for ASP.NET Framework. The nightly version of this package is available from this nightly url. The APIs are the same as those in WebApi OData V6.x, except for the new namespace
Microsoft.AspNet.OData
for V7, which is changed fromSystem.Web.OData
.Both packages depends on OData Lib 7.0.0.
The code for the packages can be found here
Known Issues
Web API OData for ASP.NET Core Beta1, has following limitations which are known issues:
- Batching is not fully supported
- Using EnableQuery in an HTTP route, i.e. non-OData route, is not fully functional
- #1175 - When you first start your service under a debugger, the project app URL will
likely make a request on a non-OData route. This will fail with an exception
Value cannot be null. Parameter name: routeName
. You can work around this issue by addingroutes.EnableDependencyInjection();
inUseMvc()
lambda inConfigure
. You can configure the default startup request in Project properties, Debug, App URL.
Web API OData for ASP.NET, there are no known issues.
OData V7.0.0 for ASP.NET Core 2.x
The new OData V7.0.0 for ASP.NET Core 2.x package supports the same features set as Web API OData V6.0.0 but works with ASP.NET Core. You can learn more about ASP.NET Core from the documentation.
To get started with OData V7.0.0 for ASP.NET Core 2.x, you can use code that is very similar to Web API OData V6.0.0. All of the documentation in Writing a simple OData V4 service is correct except for configuring the OData endpoint. Instead of using the Register() method, you’ll follow the new service + route configuration model used in ASP.NET Core.
The namespace for both Web API OData packages is Microsoft.AspNet.OData.
a. Create the Visual Studio project
In Visual Studio 2017, create a new C# project from the ASP.NET Core Web Application template. Name the project “ODataService”.
In the New Project dialog, select ASP.NET Core 2.0 and select the WebApi template. Click OK.
b. Install the OData packages
In the Nuget Package Manager, install
Microsoft.AspNetCore.OData
and all it’s dependencies.c. Add a model class
Add a C# class to the Models folder:
d. Add a controller class
Add a C# class to the Controllers folder:
In the controller, we defined a
List<Product>
object which has one product element. It’s considered as an in-memory storage of the data of the OData service.We also defined a
Get
method that returns the list of products. The method refers to the handling of HTTP GET requests. We’ll cover that in the sections about routing.This
Get
method is decorated withEnableQueryAttribute
, which in turns supports OData query options, for example$expand, $filter
etc.e. Configure the OData Endpoint
Open the file Startup.cs. Replace the existing
ConfigureServices
andConfigure
methods with the following code:f. Start the OData service
Start the OData service by running the project and open a browser to consume it. You should be able to get access to the service document at
http://host/odata/
in whichhttp://host/odata/
is the root path of your service. The metadata document can be accessed atGET http://host:port/odata/$metadata
and the products atGET http://host:port/odata/Products
wherehost:port
is the host and port of your service, usually something likelocalhost:1234
.g. Explore
As mentioned earlier, most of the samples for Web API OData V6.0.0 apply to Web API OData V7.0.0. One of the design goals was to keep the API between the two as similar as possible. While the APIs are similar, they are not identical due to differences between ASP.NET and ASP.NET Core, such as HttpRequestMessage is now HttpRequest.
ODataRoutingConvention
Both
Microsoft.AspNet.OData
andMicrosoft.AspNetCore.OData
packages support same set of OData routing conventions, including default built-in routing conventions and attribute rounting convention, so that each request can be routed to matching controller for processing. All routing conventions implement the interfaceIODataRoutingConvention
, however, with different definitions, as highlighted below, for the two packages due to different route matching implementations based on ASP.NET Framework and ASP.NET Core:- For ASP.NET Framework:
- For ASP.NET Core 2.x:
Specific routing convention, e.g. MetadataRoutingConvention, typically implements the package-specific interface, provides package-specific implementation, and shares common logic for both platform in the
Microsoft.AspNet.OData.Shared
shared project. -
14.2 Simplified optional-$-prefix for OData query option for WebAPI query parsing
Since ODL-6.x, OData Core Library supports query option with optional-$-prefix as described in this docs.
Corresponding support on WebAPI layer is available starting WebAPI-7.4.
As result, WebAPI is able to process OData system query with optional $-prefix, as in “GET ~/?filter=id eq 33” with injected dependency setting:
ODataUriResolver.EnableNoDollarQueryOptions = true.
ODL Enhancement
A public boolean attribute EnableNoDollarQueryOptions is added to ODataUriResolver. Public attribute is needed for dependency injection on the WebAPI layer.
public class ODataUriResolver { ... public virtual bool EnableNoDollarQueryOptions { get; set; } ... }
WebAPI optional-$-prefix Setting using Dependency Injection
WebAPI service injects the setting using the ODataUriResolver during service initialization: Builder of service provider container sets the instantiated ODataUriResover config using dependency injection.
ODataUriResolver resolver = new ODataUriResolver { EnableNoDollarQueryOptions = true, EnableCaseInsensitive = enableCaseInsensitive }; spContainerBuilder.AddService(ServiceLifetime.Singleton, sp => resolver));
Note that UriResolver is typically a singleton for the service instance, since each instance should follow the same Uri convention. In case of other injected dependencies that are configurable per request, scoped dependency should be used.
WebAPI Internal Processing of optional-$-prefix Setting
- WebAPI EnableQuery attribute processing instantiates WebAPI’s ODataQueryOptions object for incoming request.
- The ODataQueryOptions constructor pins down the optional-$-prefix setting (see _enableNoDollarSignQueryOptions) from the injected ODataUriResolver.
- Based on the optional-$-prefix setting, ODataQueryOptions parses the request Uri in WebAPI layer accordingly.
-
14.3 WebApi URI parser default setting updates: case-insensitive names and unqualified functions & actions
OData Core Library v7.x has introduced the following two usability improvement:
-
Uri parsing with case-insensitive name, and
-
Unqualified functions & actions, which are not required to have namespace prefix.
Starting with WebAPI OData v7.0, these two behaviors are supported by default.
Examples
Prior to WebApi v7.0, for example, the following Uris are supported:
-
GET /service/Customers?$filter=Id eq 5
-
POST /service/Customers(5)/Default.UpdateAddress()
With WebApi v7.0 by default, in addition to above, the following variances are also supported:
-
GET /service/Customers?$filter=id eq 5
-
GET /service/CUSTOMERS?$filter=Id eq 5
-
POST /service/Customers(5)/UpdateAddress()
and the combination of both case-insensitive and unqualified functions & actions, such as:
- POST /service/CUSTOMERS(5)/UpdateAddress()
Backward Compatibility
Case-insensitive semantics is supported for type name, property name and function/action name. WebAPI OData will first try to resolve the name with case-sensitive semantics and return the best match if found; otherwise case-insensitive semantics are applied, returning the unique match or throwing an exception if multiple case-insensitive matches exist.
With support for unqualified function & action, the URI parser will do namespace-qualified function & action resolution when the operation name is namespace-qualified; otherwise all namespaces in the customer’s model are treated as default namespaces, returning the unique match or throwing an exception if multiple unqualified matches exist.
Because of the precedence rules applied, scenarios supported in previous versions of WebAPI continue to be supported with the same semantics, while new scenarios that previously returned errors are also are now supported.
Please note that, even though case-insensitive and unqualified function & action support is added as a usability improvement, services are strongly encouraged to use names that are unique regardless of case, and to avoid naming bound functions, actions, or derived types with the same name as a property of the bound type. For example, a property and unqualified function with same name would resolve to a property name when the unqualified function may have been expected.
Restoring the original behavior
Even though the new behavior is backward compatible for most scenarios, customers can configure WebAPI to enforce case sensitivity and namespace qualification, as in 6.x, using dependency injection:
// HttpConfiguration configuration IServiceProvider rootContainer = configuration.CreateODataRootContainer(routeName, builder => builder.AddService<ODataUriResolver>(ServiceLifetime.Singleton, sp => new ODataUriResolver());
The above code replaces the ODataUriResolver service that supports case-insensitivity and unqualified names with a default instance of ODataUriResolver that does not.
-
-
14.4 OData Web API 7.0 (.NET Core and .NET Classic)
We’re happy to announce the release of ASP.NET Web API OData 7.0 on the NuGet gallery!
ASP.NET Web API OData 7.0 is available in two packages:
- Microsoft.AspNetCore.OData is based on ASP.NET Core 2.0.
- Microsoft.AspNet.OData is based on ASP.NET Web API.
Detail information on Web API OData 7.0 is available here
Get started with ASP.NET Web API OData 7.0 today!
Download this release
You can install or update the NuGet packages for OData Web API v7.0.0 using the Package Manager Console:
PM> Install-Package Microsoft.AspNetCore.OData
or
PM> Install-Package Microsoft.AspNet.OData
What’s in this release?
ASP.NET Core 2.0 support
See main issues from:
-
[ Web API issue #939 ] .NET Core Support.
-
[ Web API issue #772 ] Roadmap for OData WebAPI run on ASP.NET Core.
-
[ Web API issue #229 ] Port ASP.NET Web API OData to ASP.NET 5/MVC 6.
Getting started ASP.NET Core OData from here, get samples from here.
New features
-
[ PR #1497 ] Support
In
operator. -
[ PR #1409 ] Set default to Unqualified-function/acition call and case insensitive.
-
[ PR #1393 ] Set default to enable KeyAsSegment.
-
[ Issue #1503 ] Enable support for Json Batch.
-
[ Issue #1386 ] Use TemplateMatcher for ODataBatchPathMapping.
-
[ Issue #1248 ] Allow recursive complex types in OData model builder.
-
[ Issue #1225 ] Support optional dollar sign.
-
[ Issue #893 ] Support aggregation of Entity Set.
Breaking changes
-
Both Microsoft.AspNetCore.OData and Microsoft.AspNet.OData are using “Microsoft.AspNet.OData” namespace.
-
[ PR #1489 ] Change the OptionalReturn to ReturnNullable in OperationConfiguration.
-
[ PR #1353 ] Change OptionalParameter as Nullable .
Improvements & Fixes:
-
[ Issue #1510 ] move $top & $skip execute as late as possible.
-
[ Issue #1489 ] Return Task<> from method of controller doesn’t pick up odata output formatter (Core).
-
[ Issue #1407 ] Fix the BaseAddressFactory in ODataMediaTypeFormatter .
-
[ Issue #1471 ] Issue in non-odata routing with DataContact & DataMember.
-
[ Issue #1434 ] Add OData-Version into the No-Content response header.
-
[ Issue #1398 ] Expose underlying semantic OData path.
-
[ Issue #1388 ] Make Match as virtual in ODataPathRouteConstraint.
-
[ Issue #1387 ] Move Instance to select all.
-
[ Issue #1313 ] Batch requests are incorrectly routed when ASP.NET Core OData web application has any BasePath.
-
[ Issue #1263 ] Patch nested strucutural resources.
-
[ Issue #1247 ] Fix Spatial post/update problem.
-
[ Issue #1113 ] ODataResourceDeserializer no longer supports null ComplexType values.
-
[ Issue #822 ] Fix for memory leak in EdmLibHelpers.
Questions and feedback
You and your team are warmly welcomed to try out this new version if you are interested in the new features and fixes above. You are also welcomed to contribute your code to OData Web API repository. For any feature request, issue or idea please feel free to reach out to us at GitHub Issues.
-
14.5 OData Web API 7.0.1 (.NET Core and .NET Classic)
The NuGet packages for ASP.NET Web API OData v7.0.1 are available on the NuGet gallery.
You can install or update the NuGet packages for OData Web API v7.0.1 using the Package Manager Console:
PM> Install-Package Microsoft.AspNetCore.OData -Version 7.0.1
or
PM> Install-Package Microsoft.AspNet.OData -Version 7.0.1
What’s in this release?
New Features:
-
[ Web API issue #1488 ] Support optional parameters
-
[ Web API PR #1527 ] Add UseOData() extension methods
Improvements and fixes:
-
[ Web API issue #1518 ] Fix the Newtonsoft.Json dependency problem in classic version
-
[ Web API issue #1529 ] Fix ETag missing on derived Entity Set
-
[ Web API issue #1532 ] Fix JSON batch repsonse content type
Questions and feedback
You and your team are warmly welcomed to try out this new version if you are interested in the new features and fixes above. You are also welcomed to contribute your code to OData Web API repository. For any feature request, issue or idea please feel free to reach out to us at GitHub Issues.
-
-
14.6 OData Web API 7.1.0 (.NET Core and .NET Classic)
The NuGet packages for ASP.NET Web API OData v7.1.0 are available on the NuGet gallery.
You can install or update the NuGet packages for OData Web API v7.1.0 using the Package Manager Console:
PM> Install-Package Microsoft.AspNetCore.OData -Version 7.1.0
or
PM> Install-Package Microsoft.AspNet.OData -Version 7.1.0
Important Notes:
issue #1591 fixes an issue where types created by the ODataModelBuilder did not respect the namespace of the ModelBuilder and instead used the namespace of the CLR type. With PR #1592, OData WebAPI 7.1.0 now correctly uses the namespace on the ModelBuilder, if it has been explicitly set. In order to retain the old behavior of using the namespace of the CLR type, do not set the namespace on the ModelBuilder, or set the namespace on the ModelBuilder to null or to the desired namespace of the CLR type.
What’s in this release?
New Features:
-
[ #1631 ] Don’t require keys for singleton instances
-
[ #1628 ] Allow adding new members to a collection through a POST request
-
[ #1591 ] Support namespaces in OData ModelBuilder
-
[ #1656 ] Allowing the definition of partner relationships
Improvements and fixes:
-
[ #1543 ] Json batch response body if 404
-
[ #1555 ] aspnetcore ETagMessageHandler throws then ClrType property name and EdmModel property name differs
-
[ #1559 ] Don’t assume port 80 for nextLink when request was HTTPS
-
[ #1579 ] Star select ( $select= * ) not returning dynamic properties
-
[ #1588 ] Null check on ODataQueryParameterBindingAttribute for Net Core
-
[ #736 ] EDMX returned from $metadata endpoint has incorrect “Unicode” attributes
-
[ #850 ] ODataConventionModelBuilder takes into account [EnumMember] on enum members, but ODataPrimitiveSerializer (?) does not, causing mismatch in the payload.
-
[ #1612 ] Add the iconUrl to the nuget spec
-
[ #1615 ] fix compile error in sample project
-
[ #1421 ] Improve error message when structured type is received and properties are in incorrect case
-
[ #1658 ] Fix the security vulnerabilities in project dependencies
-
[ #1637 ] Change Request: Remove null check on ‘ODataFeature().TotalCount’ property
-
[ #1673 ] Respect preference header for paging
-
[ #1600 ] ActionDescriptorExtensions is not thread safe
-
[ #1617 ] Take and Top query use wrong Provider.CreateQuery Method
-
[ #1659 ] Odata V4 group by with Top and skip not working
-
[ #1679 ] Fix typo routePrerix
Questions and feedback
You and your team are warmly welcomed to try out this new version if you are interested in the new features and fixes above. You are also welcomed to contribute your code to OData Web API repository. For any feature request, issue or idea please feel free to reach out to us at GitHub Issues.
-