Hello everyone, today I would like to present the use of OData (Open Data Protocol), a protocol for developing APIs, which will turn this development an incredible task. A little bit of the history of this protocol, in 2007 Microsoft had the idea to provide an innovative way for services that provide data to serve their customers a flexibility beyond it was specifically prepared to offer, the goal was to provide the ability to the customers request data and operations that really fits their needs. In 2010 Microsoft in conjunction with other companies standardized this protocol and recently OData was made available to Asp.Net Core.
Before we take further, this article was previously published in Portuguese on Medium, so if do you prefer to read in Portuguese there would be better an option for you Medium.
With the arrival of the OData protocol to Asp.Net Core, we can create fully flexible APIs allowing consumers to request various operations such as field selection, filter operations, sorting, counting, paging, reading related entities, among many others. In this post I will be showing some of them, as also functions and logical operators to use with this library.
For this we will be creating an Api in Asp.Net Core, I will not go into many details of the development of this api, after all this is not the main goal of the post. But I will need to explain some points before we get started with OData.
We will work with 2 domain entities Developer and TaskToDo. The developer class, in addition to the name field, will have a property that will set, whether it's Front-End, Back-End, or FullStack developer and a toDo list.
Developer Types
Base class for Entities
Developer class
TaskToDo class
Now we will create a Controller that will have the registration and the search operation for all the developers or only one settled by the selection Id. The database context is solved by native dependency injection of Asp.Net Core and we will make a directy use of the Developer DbSet for Create and Get operations.
DeveloperController class
In the StartUp class I've added only the dependency injection for the database that was totally configured in the DatabaseContext class. If you would like to understand more about this configuration and about DataSeeding, at the end of this post you will find a link to my GitHub with the complete project. All classes related to database settings are in the Data folder.
Startup class
In the database I already have some developers registered and their respective Tasks, using the Get method of DeveloperController we can load all of them.
Contextualizing needs VS problems
The implementation of the Get method for load developers is working as expected, bringing the whole list of them, but imagine now that the Developer class does not have only 4 fields but dozens of them, all the requests for that API method should return all the fields of developer entity?
Suppose a auto-complete search, searching by the field name of the developer, it would be completely unnecessary to have all developer fields loaded in the payload of the response.
Now imagine another situation where the client needs to load the list of tasks of some developer and in addition only going to use the fields Title and DeadLine.
To solve these needs we would have two options.
First, develop a method for each need and in the back-end treat the data, which would be impractical, because in the future there may be many clients with different needs. Second option, treat the data on the front-end for each server response, which is done in most of the cases.
Implementing ODATA - Open Data Protocol
The simplest and fastest way to create flexible APIs in Asp.Net Core is by using the Microsoft.AspNetCore.OData package, in this post we will be using version 7.1.0 which is the latest version at the time of writing this post.
Visual Studio
- Install-Package Microsoft.AspNetCore.OData -Version 7.1.0
.net cli
- dotnet add package Microsoft.AspNetCore.OData --version 7.1.0
It's very simple to configure OData in Asp.Net Core we just need to make 3 changes to the Startup class.
Adding OData in the Startup class
With configurations 1, 2 and 3 we have already enabled the use of OData. Now for we enjoy all the power of this protocol we just need to add an attribute to the DeveloperController's methods.
Enabling OData Query
Resolving AutoComplete Search
Solving the first problem, the auto-complete search to return only the Name of the developer.Let's introduce the first operator:
- $select, makes possible to select the fields that we want to return.
https://localhost:44334/api/developer?$select=Name
Now filtering by developer name.
- $filter, perform filters like Where together with a logical operator such as:
- Lt (lower than)
- Le (lower and equal)
- Gt (greater than)
- Ge (greater and equal)
- Eq (equal)
- Ne (not equal)
https://localhost:44334/api/developer?$select=Name&$filter=name eq 'Adler Pagliarini'
Our goal has not yet been reached, in this case we are searching by the full name, not for a part of the name, so let's use a function from OData, the Contains method going to help us.
https://localhost:44334/api/developer?$select=Name&$filter=contains(name, 'Adler') eq true
Looking for related tasks
Our second goal is to be able to bring the tasks related to a developer and select only the Title and DeadLine fields.
- $expand, allows us to bring the related entities.
https://localhost:44334/api/developer?$select=Name,devType&$expand=TasksToDo
Well, we are already capable to bring the tasks related to each developer, now let's bring only it's title and deadLine.
- Operator $expand in conjunction with $select, brings the related entity and defines which fields should be listed.
https://localhost:44334/api/developer?$select=Name,devType&$expand=TasksToDo($select=Title,DeadLine)
Other operators
- $orderBy this operator can be used to bring the data in ascending or descending order.
https://localhost:44334/api/developer?$orderBy=Name Asc
or
https://localhost:44334/api/developer?$orderBy=Name Desc
ASC is not required
- $maxTop(null), enables the use of the $top operator without limiting the number of rows that you can ask to load per request
- $maxTop([int]), delimits the maximum value for the $top operator
- $skip, can be used to skip rows
https://localhost:44334/api/developer/?$top=1&$skip=1
Filtering Enum-type values
With the current configuration of OData that we've made in the StartUp class it is not possible to perform the search from values type as Enum. When trying to perform the search by all Front-End developers like for example:
https://localhost:44334/api/developer/?$filter=DevType eq 1
You will come across the following error:
The query specified in the URI is not valid.
A binary operator with incompatible types was
detected.
Found operand types ‘OData.Domain.DevType’ and ‘Edm.Int32’ for operator kind ‘Equal’.
In order to solve this problem we will have to add some settings for OData.
Adding Enum configurations to OData
We've added two configurations services in dependency injection of the OData routes. The first service is StringAsEnumResolver, with it, is possible to perform searches for an Enum value by turning it a string value, and the second service that we had to configure was to disable the Case Sensitive UnqualifiedCallAndEnumPrefixFreeResolver that was activated when we added the first service.
https://localhost:44334/api/developer/?$filter=DevType eq '1'
Note that the query parameter is enclosed in single quotation marks, because it is now treated as a string type.
Configuring OData Individually for Each Entity Domain
The operations enabled in the Startup class are global for all methods signed with EnableQuery notation, however we can enable specifics and differents operations for each domain entity. For this we will have create a class that will perform such configuration for each domain entity and return an object of type ODataConventionModelBuilder.
Configuring OData for Each Entity Domain
- Expand(SelectExpandType.Automatic), going to enables the load of related entities in automatic mode, so all queries going to return the related tasks.
// routerBuilder.Select().Filter().OrderBy().Expand().MaxTop(null);
Ending StartUp class
...
var modelConfig = ODataEntitiesConfiguration.GetEdmModel(app.ApplicationServices);
builder.AddService(Microsoft.OData.ServiceLifetime.Singleton, typeof(IEdmModel), sp => modelConfig)
...
Conclusion
My goal was to introduce you to the use of the OData protocol in Asp.Net Core in a simple, fast and practical way. There are different ways to configure the Open Data Protocol, for example, to create a different path for OData API methods, however the settings applied in this post are intended to allow APIs that are already in full operation to continue to offer what they already offer, but now with the plus of Open Data Protocol without having to rewrite them. I hope that you have enjoyed as much as I enjoyed this new feature for Asp.Net Core.
The example was based on a C# WebApi using .NET Core 2.2 and it's available in my GitHub. Feel free to fork the repository and test the implementation.
It is worth mentioning that so far it is not possible to integrate OData with Swagger in a transparent way, I believe that in the future we will support this integration, in case you know a workaround, please leave your comment so we can share it with others.
Casinos Near Foxwoods - MapyRO
ReplyDeleteFind Casinos Near Foxwoods and other 나주 출장마사지 local gems across North 충주 출장안마 America. 고양 출장마사지 A map showing casinos and other places to stay 영주 출장안마 closest to the Foxwoods 울산광역 출장마사지 Casino.