OData OData

Integrate OData to Net 5: How to make your APIs more flexible and simple

In this article we do a basic review of the OData protocol that simplifies our APIs and makes them more flexible. We’ll cover when to use this protocol and what the small caveats we need to take into account when implementing it are.

What is OData?

OData (Open Data Protocol) is an ISO/IEC approved OASIS standard that defines a set of best practices for building and utilizing RESTful APIs. OData helps you focus on your business logic while building RESTful APIs without having to worry about the various approaches for defining request and response headers, status codes, HTTP methods, URL conventions, media types, payload formats, query options, etc.OData also provides guidance for tracking changes, defining functions/actions for reusable procedures, and sending asynchronous/batch requests.OData RESTful APIs are easy to utilize. The OData metadata, a machine-readable description of the API data models, enables the creation of powerful generic client proxies and tools.

When should I use OData ?

As APIs continue to explode, each organization releases its own unique REST/SOAP/Bulk API to consume their data. And some of them also come up with their own unique query languages, such as ROQL (Oracle Service Cloud), SOQL (Salesforce), etc. This makes it difficult for an enterprise and its development team to learn and code against all these different APIs.This is where OData is very useful. OData advocates a standard way of implementing REST APIs, enabling SQL-like querying capabilities when using these RESTful APIs. OData is essentially SQL for the web built on top of standard protocols – HTTP, JSON & ATOM – while leveraging the REST architecture style.

OData Example implementation

First of all make sure to install the following nuggets:

  • Microsoft.AspNetCore.Mvc.NewtonsoftJson Version 7.0.2
  • Microsoft.AspNetCore.OData Version 7.5.5

Let’s start by taking a look at the Student Controller.

[Route("api/[controller]")]
[ApiController]
public class StudentsController : ControllerBase
{
[HttpGet]
[EnableQuery]
public IEnumerable GetStudents() => new List()
{
new Student(1,"Josh","McCall",new DateTime(1990,7,15),true,95),
new Student(2,"Kailu","Hu",new DateTime(1984,7,27),false,60),
new Student(3,"Mayra","Stephenson",new DateTime(1977,1,1),true,87),
new Student(4,"Winston","Smith",new DateTime(1984,1,27),true,100),
new Student(5,"Sophia","Willams",new DateTime(2000,2,8),false,99),
new Student(6,"Jacob","Mason",new DateTime(2002,1,2),true,55),
new Student(7,"Olivia","Macklemore",new DateTime(2001,9,11),false,23)
};
}

Now, let’s make use of Net 5 syntax and write the Student record.

namespace ODataWithNet5Demo.Models
{
public record Student(int Id,
string FirstName,
string LastName,
DateTime BirthDate,
bool IsAdvanced,
int Grade);
}

Next, we need to register OData services in the ConfigureServices startup method.

public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
services.AddOData();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "odata demo", Version = "v1" });
});
}

Also finally add this to the Startup Configure method. 

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "odata v1"));
}

app.UseHttpsRedirection();

app.UseRouting();

app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.EnableDependencyInjection();
endpoints.Select().Filter().Expand().Count().OrderBy().MaxTop(100);
});
}

This is the bare minimum we need to start making requests using OData.

If you go to the browser you can query:  https://localhost:44319/api/students?$filter=grade gt 90

You will get a list of students with grades above 90. Remember that this feature does not require any parameter in the GetAllStudents() Action method in the controller. This feature is added by the [EnableQuery] attribute. This makes your code simpler because now you don’t need a GetStudent(id), GetAllStudents(), or GetStudentsByGrade(grade) endpoint- you have all the functionality you need in one single endpoint.
OData 1

What if I need to use $select?

If you want to use the $select filter to retrieve only the properties you need, you’ll have to install the
Microsoft.AspNetCore.Mvc.NewtonsoftJson Nuget and add the following:

public void ConfigureServices(IServiceCollection services)
{
services.AddControllers().AddNewtonsoftJson();
services.AddOData();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "odata", Version = "v1" });
});
}

Then, if you go to the browser you can query https://localhost:44319/api/students?$select=firstname

OData 2

Does OData support pagination?

OData has support for pagination, but in order to make it work we need to make a few more changes to the OData configuration. You need to configure the EDM (Entity Data Model). The data model is the basis of an OData service.If you want to use the $select filter to retrieve only the properties you need, you’ll have to install the
Microsoft.AspNetCore.Mvc.NewtonsoftJson Nuget and add the following:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "ODataWithNet5Demo v1"));
}
app.UseHttpsRedirection();
app.UseMvc(routeBuilder =>
{
routeBuilder.Select().Filter().Expand().MaxTop(100).Count().OrderBy();
routeBuilder.EnableDependencyInjection();
routeBuilder.MapODataServiceRoute("odata", "odata", GetEdmModel());
});
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
private static IEdmModel GetEdmModel()
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet("Students");
return builder.GetEdmModel();

Then if you go to the browser, you can query https://localhost:44319/odata/students?$select=firstname&$count=true&$top=3

O Data 3
Notice that the url is not /api/students , but /odata/students. In this case we are using the url configured in the OData Service Route. This url gives us access to OData metadata, such as the context and the count, as we can see from the previous image. We can use the count value to configure paginators in the frontend in the event of having a lot of rows to display. In this example, we are also using the $top operator which we can use as the page size.

Note: Make sure to name the action method in the Controller GetStudents() (Get[EntityName]) so that the OData EDM Model can find it. If you name the action method something like GetAllStudents() you will get an error. You can take a look at the source code here. 

Javier A

About the Author

Javier Acrich a multi-talented .NET Developer capable of managing both front-end and back-end development. Successfully creates and modifies web features to improve functioning for clients. Skilled in architecture and design of web applications and solutions.