UPDATE 2 @1:21 pm on 16th August (PST):
There is an updated version of the nuget package that resolves the previous dependency issues. Oh and my comments are now working again.

UPDATE 1 @10:00 am on 16th August (PST):
If you’ve tried using the preview nuget package and had problems, rest assured we are working on the issue (which is a dependency version issue). Essentially the preview and one of its dependencies have conflicting dependencies on specific versions of packages. The ETA for a fix that resolves this nuget issue is later today. 
Also if you’ve made a comment and it isn’t showing up, don’t worry it will. I’m currently having technical problems approving comments.

Earlier versions ASP.NET Web API included basic support for the OData, allowing you to use the OData Query Options $filter, $orderby, $top and $skip to shape the results of controller actions annotated with the [Queryable] attribute. This was very useful and worked across formats. That said true support for the OData format was rather limited.  In fact we barely scratched the surface of OData, for example there was no support for creates, updates, deletes, $metadata and code generation etc.

To address this we’ve create a preview of a new nuget package (and codeplex project) for building OData services that:

  • Continues to support the [Queryable] attribute, but also allows you to drop down to an Abstract Syntax Tree (or AST) representing $filter & $orderby.
  • Adds ways to infer a model by convention or explicitly customize a model that will be familiar to anyone who’s used Entity Framework Code First.
  • Adds support for service documents and $metadata so you can generate clients (in .NET, Windows Phone, Metro etc) for your Web API.
  • Adds support for creating, updating, partially updating and deleting entities.
  • Adds support for querying and manipulating relationships between entities.
  • Adds the ability to create relationship links that wire up to your routes.
  • Adds support for complex types.
  • Adds support for Any/All in $filter.
  • Adds the ability to control null propagation if needed (for example to avoid null refs working about LINQ to Objects).
  • Refactors everything to build upon the same foundation as WCF Data Services, namely ODataLib.


In fact this is an early preview of a new OData Server stack built to take advantage of Web APIs inherent flexibility and power which compliments WCF Data Services.

This preview ships with a companion OData sample service built using Web API. The sample includes three controllers, that each expose an OData EntitySet with varying capabilities. One is rudimentary supporting just query and create, the other two are more complete, supporting Query, Create, Update, Patch, Delete and Relationships. The first complete example does everything by hand, the second derives from a sample base controller called EntitySetController that takes care of a lot of the plumbing for you and allows you to focus on the business logic.

The rest of this blog post will introduce you to the components that make up this preview and how to stitch them together to create an OData service, using the code from this sample from the ASP.NET Web Stack Sample repository if you want to follow along.

[Queryable] aka supporting OData Query

If you want to support OData Query options, without necessarily supporting the OData Formats, all you need to do it put the [Queryable] attribute on an action that returns either IQueryable<> or IEnumerable<>, like this:

[Queryable]
public IQueryable<Supplier> GetSuppliers()
{
    return _db.Suppliers;
}

Here _db is an EntityFramework DBContext. If this action is routed to say ~/Suppliers then any OData Query options applied to that URI will be applied by an Action Filter before the result is sent to the client.

For example this: ~/Suppliers?$filter=Name eq ‘Microsoft’

Will pass the result of GetSuppliers().Where(s => s.Name == “Microsoft”) to the formatter.

This all works as it did in earlier previews, although there are a couple of, hopefully temporary, caveats:

  • The element type can’t be primitive (for example IQueryable<string>).
  • Somehow the [Queryable] attribute must find a key property. This happens automatically if your element type has an ID property, if not you might need to manually configure the model (see setting up your model).

Doing more OData

If you want to support more of OData, for example, the official OData formats or allow for more than just reads, you need to configure a few things.

The ODataService.Sample shows all of this working end to end, and the remainder of this post will talk you through what that code is doing.

Setting up your model

The first thing you need is a model. The way you do this is very similar to the Entity Framework Code First approach, but with a few OData specific tweaks:

  • The ability to configure how EditLinks, SelfLinks and Ids are generated.
  • The ability to configure how links to related entities are generated.
  • Support for multiple entity sets with the same type.


If you use the ODataConventionModelBuilder most of this is done automatically for you, all you need to do is tell the builder what sets you want, for example this code:

ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Product>("Products");
modelBuilder.EntitySet<ProductFamily>("ProductFamilies");
modelBuilder.EntitySet<Supplier>("Suppliers");
IEdmModel model = modelBuilder.GetEdmModel();

Builds a model with 3 EntitySets (Products, ProductFamilies and Suppliers) where the EntityTypes of those sets are inferred from the CLR types (Product, ProductFamily and Supplier) automatically, and where EditLinks, SelfLinks and IdLinks are all configured to use the default OData routes.

You can also take full control of the model by using the ODataModelBuilder class, here you explicitly add EntityTypes, Properties, Keys, NavigationProperties and how to route Links by hand. For example this code:

var products = modelBuilder.EntitySet<Product>("Products");
products.HasEditLink(entityContext => entityContext.UrlHelper.Link( 
    ODataRouteNames.GetById, 
    new { controller = "Products", id = entityContext.EntityInstance.ID } 
));
var product = products.EntityType;
product.HasKey(p => p.ID);
product.Property(p => p.Name);
product.Property(p => p.ReleaseDate);
product.Property(p => p.SupportedUntil);

Explicitly adds a EntitySet called Products to the model, configures the EditLink (and unless overridden the SelfLink and IdLink) generation so that if uses the ODataRouteNames.GetById route, and then as you can see the code needed to configure the Key and Properties is very similar to the Code First.

For a more complete example take a look at the GetExplicitEdmModel() method in the sample it builds the exact same model as the ODataConventionModelBuilding by hand.

Setting up the formatters, routes and built-in controllers

To use the OData formats you first need to register an ODataMediaTypeFormatter, which will need the model you built previously:

// Create the OData formatter and give it the model
ODataMediaTypeFormatter odataFormatter = new ODataMediaTypeFormatter(model);

// Register the OData formatter
configuration.Formatters.Insert(0, odataFormatter);

Next you need to setup some routes to handle common OData requests, below are the routes required for a Read/Write OData model built using the OData Routing conventions that also supports client side code-generation (vital if you want a WCF DS client application to talk to your service).

// Metadata routes to support $metadata and code generation in the WCF Data Service client.
configuration.Routes.MapHttpRoute(
   
ODataRouteNames.Metadata,
    "$metadata",
    new { Controller = "ODataMetadata", Action = "GetMetadata" }
);
configuration.Routes.MapHttpRoute(
    ODataRouteNames.ServiceDocument,
    "",
    new { Controller = "ODataMetadata", Action = "GetServiceDocument" }
);

// Relationship routes (notice the parameters is {type}Id not id, this avoids colliding with GetById(id)).
// This code handles requests like ~/ProductFamilies(1)/Products

configuration.Routes.MapHttpRoute(ODataRouteNames.PropertyNavigation, "{controller}({parentId})/{navigationProperty}");

// Route for manipulating links, the code allows people to create and delete relationships between entities
configuration.Routes.MapHttpRoute(ODataRouteNames.Link, "{controller}({id})/$links/{navigationProperty}");

// Routes for urls both producing and handling urls like ~/Product(1), ~/Products() and ~/Products
configuration.Routes.MapHttpRoute(ODataRouteNames.GetById, "{controller}({id})");
configuration.Routes.MapHttpRoute(ODataRouteNames.DefaultWithParentheses, "{controller}()");
configuration.Routes.MapHttpRoute(ODataRouteNames.Default, "{controller}");

One thing to note is the way that the ODataRouteNames.PropertyNavigation route attempts to handle requests to urls like ~/ProductFamilies(1)/Products and ~/Products(1)/Family etc. Essentially a single route for all Navigations. For this to work without requiring a single Action for all navigation properties, we register a custom action selector that will build the Action name using the {navigationProperty} parameter of the PropertyNavigation route:

// Register an Action selector that can include template parameters in the name
configuration.Services.Replace(typeof(IHttpActionSelector), new ODataActionSelector());

This custom action selector will dispatch a request to GET ~/ProductFamilies(1)/Products, to an action called the GetProducts(int parentId) on the ProductFamilies controller.

At this point our previous GetSupplier action can return OData formats. However it will still produce links that won’t work when dereferenced. To fix this we need to start creating our controllers.

Adding Support for OData requests

In our model we added 3 entitysets: Products, ProductFamilies and Suppliers. So first we create 3 controllers, called ProductController, ProductFamiliesController and SuppliersController respectively.

Queries

By convention your controllers should have a method called Getxxx() that returns IQueryable<T>. Here the T is the CLR type backing your EntitySet, so for example on the ProductsController which is for the Products entityset the T should be Product, and the action should look something like this:

[Queryable]
public IQueryable<Product> GetProducts()
{
    return _db.Products;
}

As you can see this is the same as before, but now it is participating in a ‘compliant’ OData service. Inside this method you can do all sorts of things, you can add additional filters based on the identity of the caller, you do auditing, you can do logging, you can aggregate data from multiple sources.

There is even a way to drop down a layer so you get to the see the OData query that has been received. Doing this allows you to process the query yourself in whatever way is appropriate for your data sources. For example you might not have an IQueryable you can use. See ODataQueryOptions for more on this.

At this point because you’ve registered a model and you’ve setup routes for $metadata and the OData Service Document, you should be able to use Visual Studio’s Add Service Reference to generate a set of client proxy classes that you could use like this:

foreach(var product in ctx.Products.Where(p => p.Name.StartsWith(“MS”))
{
     Console.WriteLine(product.Name);
}

WCF DS client will then translate this into a GET request like this:

~/Products$filter=startswith(Name,’MS’)

And everything should just work.

Get by Key

To retrieve an individual item using it’s key in OData you send a GET request to a url like this: ~/Products(1). The ODataRouteNames.GetById route handles requests like this:

public HttpResponseMessage GetById(int id)
{
    Supplier supplier = _db.Suppliers.SingleOrDefault(s => s.ID == id); 
    if (supplier == null) 
    { 
         return Request.CreateResponse(HttpStatusCode.NotFound); 
    } 
    else 
    { 
         return Request.CreateResponse(HttpStatusCode.OK, supplier); 
    }
}

As you can see the code simply attempts to retrieve an item by ID, and then return it with a 200 OK response or return 404 Not Found.

If you have WCF DS proxy classes on the client a request like this:

ctx.Products.Where(p => p.ID == 5).FirstOrDefault();

Should now hit this action.

Inserts (POST requests)

To create a new Entity in OData you POST a new Entity directly to the url that represents the EntitySet, this means that insert requests just like queries end up matching the ODataRouteNames.Default route, however Web API’s action selector picks actions prefixed with Post for POST requests to distinguish from GET requests for Queries:

public HttpResponseMessage PostProduct(Product product)
{
    product.Family = null;

    Product addedProduct = _db.Products.Add(product); 
    _db.SaveChanges(); 

    var response = Request.CreateResponse(HttpStatusCode.Created, addedProduct); 
    response.Headers.Location = new Uri(
      
Url.Link(ODataRouteNames.GetById, new { Controller = "Products", Id = addedProduct.ID })
    ); 
    return response;
}

Here we simply add the product to our Entity Framework context and then call SaveChanges(). The only interesting bit way we set the location header using the ODataRouteNames.GetById route (i.e. the default OData route for Self/Edit links).

Updates (PUT requests)

To replace an Entity in OData you PUT the updated version of the entity to the uri specified in the EditLink of the entity. This will match the GetById route, but because it is a PUT request it will look for an Action name prefixed with Put.

This example shows how to do this:

public HttpResponseMessage Put(int id, Product update)
{
    if (!_db.Products.Any(p => p.ID == id))
    {
        throw ODataErrors.EntityNotFound(Request);
    }
    update.ID = id; // ignore the ID in the entity use the ID in the URL.
    _db.Products.Attach(update);
    _db.Entry(update).State = System.Data.EntityState.Modified;
    _db.SaveChanges();
    return Request.CreateResponse(HttpStatusCode.NoContent);
}

The code is pretty simple, first we verify the entity the client is trying to update actually exists, then we ignore the ID in the entity and instead use the id extracted from the uri of the request, this stops clients updating one entity using the editlink of another entity.

Once all that validation is out of the way we attach the updated entity and tell EF it has been modified before calling save changes and finally returning a No Content response.

As you can see this leverages a helper class called ODataErrors, its job is simply to raise OData compliant errors. The sample uses the EntityNotFound method quite frequently, and it follows the general pattern for failing in an OData compliant way so lets take a peak at its implementation:

public static HttpResponseException EntityNotFound(this HttpRequestMessage request)

    return new HttpResponseException( 
        request.CreateResponse( 
            HttpStatusCode.NotFound, 
            new ODataError 
            { 
                Message = "The entity was not found.", 
                MessageLanguage = "en-US", 
                ErrorCode = "Entity Not Found." 
            } 
        ) 
    );
}

We create a HttpResponseException with a nested Not Found response, the body of which contains an ODataError. By putting an ODataError in the body we get the ODataMediaTypeFormatter to format the body as a valid OData error message as per the content-type requested by the client.

NOTE: Over time we will try to teach the ODataMediaTypeFormatter to handle HttpError as well by using a default translation from HttpError to ODataError.

Partial Updates (PATCH requests)

PUT request have replace semantics which makes updates all or nothing, meaning you have to send all the properties even if only a subset have changed.This is where PATCH comes in, PATCH allows clients to send just the modified properties on the wire, essentially allowing for partial updates.

This example shows an implementation of Patch for Products:

public HttpResponseMessage PatchProduct(int id, Delta<Product> product)
{
    Product dbProduct = _db.Products.SingleOrDefault(p => p.ID == id);
    if (dbProduct == null)
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }

    product.Patch(dbProduct);
    _db.SaveChanges();

    return Request.CreateResponse(HttpStatusCode.NoContent);
}

Notice that the method receives a Delta<Product> rather than a Product.

We do this because, if we use a Product directly and set the properties that came on the wire as part of the patch request, we would not be able to tell why a property has a default value. It could be because the request set the property to its default value or it could be because the property hasn’t been set yet. This could lead to mistakenly resetting properties the client doesn’t want reset. Can anyone say ‘data corruption’?

The Delta<Product> class is a dynamic class that acts as a lightweight proxy for a Product. It allows you to set any Product property, but it also remembers which properties you have set. It uses this knowledge when you call .Patch(..) to copy across only properties that have been set.

Given this we simply retrieve the product from the database and then we call Patch to apply the changes requested to the entity in the database. Once that is done we call SaveChanges to push the changes to the database.

Deletes (DELETE requests)

To delete an entity you send a DELETE request to it’s editlink, so this is similar to GetById, Put and Patch, except this time Web API will look for an action prefixed with Delete, something like this:

public HttpResponseMessage DeleteProduct(int id)
{
    Product toDelete = _db.Products.FirstOrDefault(p => p.ID == id);
    if (toDelete == null)
    {
        throw ODataErrors.EntityNotFound(Request);
    }
    _db.Products.Remove(toDelete);
    _db.SaveChanges();

    return Request.CreateResponse(HttpStatusCode.Accepted);
}

As you can see if the entity is found it is simply deleted and we return an Acccepted response.

Following Navigations

To address related entities in OData you typically start with the Uri of a particular entity and then append the name of the navigationProperty. For example to retrieve the Family for Product 15 you do a GET request here: ~/Products(15)/Family.

Implementing this is pretty simple:

public ProductFamily GetFamily(int parentId)
{
     return _db.Products.Where(p => p.ID == parentId).Select(p => p.Family).SingleOrDefault();
}

This matches the ODataRouteNames.PropertyNavigation which you’ll notices uses {parentId} rather than {id}, if it was id, this method would also be a match for the ODataRouteNames.GetById route, which would cause problems in the action selector infrastructure. By using parentId we sidestep this issue.

Creating and Deleting links

The OData protocol allows you to get and modify links between entities as first class resources. In the preview however we only support modifications.

To create links you POST or PUT a uri (specified in the request body) to a url something like this: ~/Products(1)/$links/Family.

The uri in the body points to the resource you wish to assign to the relationship. So in this case it would be the uri of a ProductFamily you wish to assign to Product.Family (i.e. setting Product 1’s Family).

Whether you POST or PUT depends on the cardinality of the relationship, if it is a Collection you POST, if it is a single item (as Family is) you PUT. Both are mapped through the ODataRouteNames.Link route, which is set up to handle creating links for any relationship. Unlike following Navigations it feels okay to use a single method because the return type always the same, i.e. a No Content response:

public HttpResponseMessage PutLink(int id, string navigationProperty, [FromBody] Uri link)
{
    Product product = _db.Products.SingleOrDefault(p => p.ID == id);

    switch (navigationProperty)
    {
        case "Family":
            // The utility method uses routing (ODataRoutes.GetById should match) to get the value of {id} parameter
            // which is the id of the ProductFamily.
            int relatedId = Configuration.GetKeyValue<int>(link);
            ProductFamily family = _db.ProductFamilies.SingleOrDefault(f => f.ID == relatedId);
            product.Family = family;
            break;

        default:
            throw ODataErrors.CreatingLinkNotSupported(Request, navigationProperty); 
    }
    _db.SaveChanges();

    return Request.CreateResponse(HttpStatusCode.NoContent);
}

The {navigationProperty} parameter from the route is used to decide what relationship to create, also notice the [FromBody] attribute on the Uri link parameter it is vital otherwise the link will always be null.

If you want to remove a relationship between entities you send a DELETE request to the same URL, the code for that looks like this:

public HttpResponseMessage DeleteLink(int id, string navigationProperty, [FromBody] Uri link)
{
    Product product = _db.Products.SingleOrDefault(p => p.ID == id);
    switch (navigationProperty)
    {
        case "Family":
            product.Family = null;
            break;

        default:
            throw ODataErrors.DeletingLinkNotSupported(Request, navigationProperty);
    }
    _db.SaveChanges();
    return Request.CreateResponse(HttpStatusCode.NoContent);
}

Conclusion

If you implement all these methods for each of your OData EntitySets you should have a compliant OData service. Of course this code is only at preview quality so is likely to have bugs, that said I hope you’ll agree this provides a good foundation for creating OData services.

We are currently thinking about adding support for: inheritance, OData actions & functions, etags and JSON Light etc. And of course we want to hear your thoughts so we can incorporate your feedback!

Next up

This blog post doesn’t cover everything you can do with the Preview, you can use:

  • ODataQueryOptions rather than [Queryable] to take full control of handling the query.
  • ODataResult<> to implement OData features like Server Driven Paging and $inlinecount.
  • EntitySetController<,> to simplify creating fully compliant OData entitysets.


I’ll be blogging more about these soon.

Enjoy!