piotrwalat.net

Getting started with OData services in ASP.NET Web API

7 comments

OData is an application-level protocol that has been designed to provide data interaction operations via HTTP. Besides basic data manipulation capabilities (such as adding, deleting and updating) it also provides more advanced mechanisms such as filtering and navigation between related entities.

In this post I am going to show how to leverage some of OData features introduced to ASP.NET Web API to build example service.

OData

You may be wondering why would you need another http based protocol for your web apps. Aren’t simple JSON or XML services good enough? Well in fact OData extends these and is not meant to be a replacement. It can use either XML (ATOM) or JSON to represent resources and what is important adheres to REST principles. In some sense it builds on top of ‘simple’ REST HTTP services with a very clear aim – to simplify and standardize the way we manipulate and query resources and data sets. If your application is data centric chances are you could benefit from OData. Also if you’ve struggled to create search/filter or paging api for your REST services, OData has a provides this as well.

Some examples of OData query syntax:

  • Entity set – /Artists
  • Entity by id – /Artists(1)
  • Sorting – /Artists?$orderby=Name
  • Filtering – /Artists?$filter=Name eq ‘Gridlock’

But that’s just a tip of an iceberg.

Instead of talking more, let’s write some code. Fortunately, ASP.NET Web API let’s us create OData endpoints quite easily.

Creating the project

Let’s start by creating a new ASP.NET Web API project. We won’t need any MVC related things (views, js libraries, etc.).
OData functionality is provided by a separate assembly that has to be installed separately. Please note that at the time of writing the package is still pre-release and the latest version available on official nuget repository is 0.3 RC (see this blogpost for a detailed overview of the release).
Unfortunately there is an issue when using this package with latest ODataLib preventing some functionality from working (eg. filtering). Moreover newer commits introduced some breaking changes in configuration API. Because there is no point learning deprecated API we will use nightly builds available at http://www.myget.org/F/aspnetwebstacknightly/ nuget source. If you are unsure how to configure nuget to get these, have a look here.

Once you set up nightly build nuget source, you can install latest Web API OData package using Manage NuGet Packages, jus make sure you select ‘Include Prerelease’ in the dropdown on the top.

Microsoft.AspNet.WebApi.OData nightly

Please have in mind that Web API OData support is still work in progress and may miss support for certain OData features. Having said this, it definitely provides an impressive set of functionality.

Data model

We need a simple model to operate on. I will use Entity Framework and SQL CE 4, but Web API’s OData implementation does not constraint you to any particular data persistence technology.

You can create a new SQL CE database in App_Data folder and use built in explorer to execute SQL code. Please note that it does not support execution of multiple statements so you will need to execute it one by one. Once database schema is in place we can generate Entity Data Model using the wizard provided (it should automatically detect the database created).

EntityDataModel

Entities

In the end we should get a DbContext class that will be used to perform data operations.

$metadata endpoint and IEdmModel

As I mentioned previously OData standard defines a special metadata endpoint that contains a document defining the entity sets, relationships, entity types, and operations. This makes OData service self-describing and enables client libraries to generate client-side code to represent server types and simplify service access (for example by generating proxies). Metadata endpoint should be available under /$metadata. If you are familiar with SOAP services you can think about it roughly as a WSDL analogue.

Metadata document uses OData Common Schema Definition Language (CSDL). Fortunately ASP.NET Web API can expose  $metadata endpoint for us, as long as we supply a representation of our model in the form of IEdmModel object.

You can also build model representation explicitly using ODataModelBuilder to have more fine grained control over generated representation.

Enabling OData

Microsoft.AspNet.WebApi.OData package provides a set of classes that are supposed to plug into Web API extensibility points in order to provide OData support (formatters, path handling, etc.).

The RC version had a single HttpConfiguration.EnableOData(IEdmModel) helper method that did all this in one go. However this approach wasn’t really well fitted for scenarios where we wanted to support different models in one application. Because of this latest versions use per route configuration (which is more flexible).

This code (executed from Global.asax.cs) does two things:

  • registers our model representation (IEdmModel) with a route – we are using null for route prefix, meaning it will be the root route, but we could have specified something like ‘albums’ making OData Albums services available at /albums instead of / (and thanks to this we could serve more data models in one app)
  • enables query support (EnableQuerySupport()) for actions returning IQueryable<T> (we will show what’s that about when creating controllers)

Now our service should automagically know how to handle OData ~/$metadata request. Cool, isn’t it :) ?

Controllers

Now it’s time to reap the reward. We need to add controllers that will actually expose our entities as OData resources. As you will see this is not very different from writing ‘regular’ CRUD controllers. It is very easy to expose OData entity set.

/Artists resource should now become available along with all the fancy filtering functionality for entity sets.

OData specific routing and formatting has been provided by controller attributes. Alternatively instead of deriving ArtistsController from ApiController we could have derived from ODataController which is a helper abstract class already decorated with all the necessary attributes (I would recommend this approach as additional functionality may be introduced in the future).

Now we can continue by providing other actions such as add (POST), update (PUT), partial update (PATCH) and delete.

Please note that current builds use JSON as default formatting. You can change the format to XML by using appropriate Accept header when making the request.

Security and mass assignment

In the example above we expose our model directly to the user. We also assume that users can modify data entities including all their properties without restrictions (which in this particular case is not a security problem).
In real life scenarios that involve authorization (and authentication) such an approach is very often unacceptable as we may obliviously expose properties that were never supposed to be modified by the given user. This is especially true when our model contains properties that directly affect authorization or authentication process (eg. the infamous isAdmin flag or a property denoting owner of an object). Usually in such case we need an extra layer of security and validation that will ensure a user does not execute an action he is not permitted to execute (eg. modify an object he does not own). If possible we also can introduce a flattened-out DTO model that is mapped to data model (eg. using Automapper). Such DTOs are meant to ‘hide’ persistable data model.

Testing the service

I will use Fiddler composer to test the service.

POST Genre

Note the Content-Type: application/json header. This should add a new genre. If we wanted to make a partial update to the entity we would PATCH verb as follows.

Patch

Now the genre with id=3 will have an updated description.

Finally, let’s issue a query against Artists entity set that will sort the results and return their count:
http://localhost:2537/Artists?$orderby=Name&$inlinecount=allpages

EntitySetQuery

As you can see we didn’t have to write any special logic to support this feature – all was provided by the framework. If we wanted we also could have provided custom actions in the controllers as we are not limited to OData specific crud operations.

OData is without a doubt an interesting protocol. It feels a little bit like REST services on steroids :)

The source code is available as usually on bitbucket.

Written by Piotr Walat

January 22nd, 2013 at 7:35 am

  • Pingback: Dew Drop – January 22, 2012 (#1,484) | Alvin Ashcraft's Morning Dew

  • Pingback: Scott Banwart's Blog » Distributed Weekly 191

  • http://www.facebook.com/preishuber Hannes Preishuber

    1) where did you find the Quote regarding EnableOData
    2) I didnt get the attributes OdataFormatting… any idea?
    234567891011121314151617181920[ODataRouting][ODataFormatting]

    • http://www.piotrwalat.net/ Piotr Walat

      Hi Hannes,

      Ad. 1) Its not a quote, rather my comment based on discussion on codeplex and changes to asp.net web api source code. Specifically, please have a look at this commit - https://aspnetwebstack.codeplex.com/SourceControl/changeset/555ffc1b0fca.”These changes lift the limitation on a single, global model per configuration and instead allows each OData route to be configured with a different EDM model”

      Ad. 2) It seems that perhaps you are not using latest nightly builds? (I tested the code with a build from 26th Jan and it works). Make sure you are using a nightly build for Microsoft.AspNet.WebApi.OData package. The example will not work with RC version (0.3.0).

      Please also note that this example uses pre-release version of libaries and API can still change in the future (although i suspect RTM version will be released pretty soon). This is the price we pay for playing with latest Web API bits :)

      • http://www.facebook.com/preishuber Hannes Preishuber

        hI Pitor

        thanks for fast and detailed response.
        You are right, I didt the nuget update and now it works. Web.http.odata is now v4.0.30319 . I thought I did it allready.
        In general (not your point) “latest” is not exact definition. There are too much variables in ASP.NET Web Api
        E.g. now Queryable have no longer Resultlimit :-(

        Thanks again
        -Hannes

        • http://www.facebook.com/youssefm Youssef Moussaoui

          Hi Hannes,

          ResultLimit was just renamed to PageSize. And the recommended way to create an OData controller is to just derive from the new ODataController class. It should automatically add the right routing and formatting attributes for you.

  • http://www.dotnetjalps.com/ Jalpesh Vadgama

    Nice tip