Although OData exists for more than 10 years, I've only heard about it just earlier this week. During a session on the dotNET YouTube channel, Hassan Habib talked about What's new in OData: $compute. This was my first introduction to OData, and I was immediately hooked, I had to try it out on my own.
Taken from the OData website, OData is described as:
OData (Open Data Protocol) is an ISO/IEC approved, OASIS standard that defines a set of best practices for building and consuming RESTful APIs. OData helps you focus on your business logic while building RESTful APIs without having to worry about the various approaches to define 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 consume. The OData metadata, a machine-readable description of the data model of the APIs, enables the creation of powerful generic client proxies and tools.
In my own words from what I've seen from it so far, I would describe OData as REST on steroids. The server provides a resource endpoint to its consumers with querying capabilities out of the box. If you're familiar with GraphQL, this should sound familiar.
OData simplifies and reduces the amount of code to communicate between a client and the server, without introducing complexity. Making it a win-win for the client and the server, and this also results in a better experience for the end-users.
Without going into the details, let's compare a normal REST endpoint with a REST endpoint that's "OData-fied".
Invoking the students' endpoint
/students results in the following response, an array of students.
Now, let's invoke the OData equivalent
/orest/students, which gives us the following response.
No big changes so far, only that the students' array is wrapped within the OData context. Nothing special, but now the fun can start.
We can use the OData expression syntax and append query parameters to the endpoint URL to change the behavior of the response.
As an example, let's make a change to the student's endpoint to only include the students' first and last names.
Now that we've seen a first example, how difficult do you think it would be to filter the above results to only include students that start with "A" as the last name?
Right, you've probably guessed it, not that hard.
Was I correct by telling you this would be easy?
Let's take it a step further and only select two students while skipping the first student ordered by their name, and you know what, let's throw in a total count in as well.
Most entities also include nested objects, and with OData you can also select these objects. In our example, a student is enrolled in courses. Let's take a look at what this query looks like.
These examples are great, but the best part is yet to come. How many lines of code do you think it requires to build these endpoints?
The answer to that question is zero lines of code. Yes, zero lines of code.
The students' controller has a single GET endpoint that's decorated with the
[EnableQuery] attribute, and returns an
But it even gets better. At first, I thought that the complete result set was taken in memory and that it was mutated into the result. But what blows my mind, is that because I'm using Entity Framework, the OData query syntax is translated into a SQL statement. This means that we're only selecting the bare minimum, which makes it not only convenient but also performant.
For example, the endpoint to select the paginated student names is translated into the following SQL query.
Ok, to be fair I haven't been totally honest with you. OData does require some lines of code to set it up. The good thing is, that this only takes a few minutes of your time.
The first step is to install the
Microsoft.AspNetCore.OData NuGet package.
Next, OData needs to be added to the Api controllers.
Lastly, a model builder is created to build an EDM (Entity Data Model), which acts as a schema to your model and wraps the results with the OData metadata wrapper. The EDM is available at the
And, that's it.
So why should we use OData?
I've only spent a few hours with OData, but this seems to be useful in many cases. Without OData we have to write all of the query code manually. Think about the time that could've been saved on your project if you didn't need to write and maintain this code. Not only the production code but also the code that it requires to test it. And what about the meetings between two teams to implement these features and to make modifications to them.
Besides the productivity boost, in most cases, this has a positive impact on the performance of your application. Far too often I've seen that a client retrieves too much information from the server and that it needs to add its own logic to filter or enhance the result set. This has two costs, the initial amount of data that needs to be downloaded, and the memory consumption on the client-side which can be bigger than we initially think depending on the client's specifications, e.g. a brand-new desktop VS an older phone with a low bandwidth connection. With an OData Endpoint, we can move all of this logic back to the server, which we're in control and we can make sure that it can handle the workload.
Besides the client-side dangers, writing the query logic server-side also has its own caveats. Just like on the client-side, we can retrieve too much data from the SQL server, we can write a slow query, or on rare occasions we can also introduce a bug. I'm positive to think that the generated queries are often better or equal compared to the queries that we would write on our own.
Said in short, OData simplifies and reduces the amount of code to communicate between a client and the server, without introducing complexity.
I'm looking forward to giving OData a spin on a real project.
The examples from this blog are available at timdeschryver/ODataSandboxApi, which you can also use to try OData out for yourselves.
I appreciate it if you would support me if have you enjoyed this post and found it useful, thank you in advance.