With the upcoming ASP.NET 2012.2 release, we’ll be adding support for OData to WebAPI. In this blog post, I’ll go over the three simple steps you’ll need to go through to get your first OData service up and running:
- Creating your EDM model
- Configuring an OData route
- Implementing an OData controller
Before we dive in, the code snippets in this post won’t work if you’re using the RC build. You can upgrade to using our latest nightly build by taking a look at this helpful blog post.
1) Creating your EDM model
First, we’ll create an EDM model to represent the data model we want to expose to the world. The ODataConventionModelBuilder class makes this this easy by using a set of conventions to reflect on your type and come up with a reasonable model. Let’s say we want to expose an entity set called Movies that represents a movie collection. In that case, we can create a model with a couple lines of code:
1: ODataConventionModelBuilder modelBuilder = new ODataConventionModelBuilder();
2: modelBuilder.EntitySet<Movie>("Movies");
3: IEdmModel model = modelBuilder.GetEdmModel();
2) Configuring an OData route
Next, we’ll want to configure an OData route. Instead of using MapHttpRoute the way you would in WebAPI, the only difference here is that you use MapODataRoute and pass in your model. The model gets used for parsing the request URI as an OData path and routing the request to the right entity set controller and action. This would look like this:
1: config.Routes.MapODataRoute(routeName: "OData", routePrefix: "odata", model: model);
The route prefix above is the prefix for this particular route. So it would only match request URIs that start with http://server/vroot/odata, where vroot is your virtual root.
3) Implementing an OData controller
Finally, we just have to implement our MovieController. Instead of deriving from ApiController, you’ll need to derive from ODataController. ODataController is a new base class that wires up the OData formatting and action selection for you. Here’s what an implementation might look like:
1:publicclass MoviesController : ODataController
2: {
3: List<Movie> _movies = TestData.Movies;
4:
5: [Queryable]
6:public IQueryable<Movie> Get()
7: {
8:return _movies.AsQueryable();
9: }
10:
11:public Movie Get([FromODataUri] int key)
12: {
13:return _movies[key];
14: }
15:
16:public Movie Patch([FromODataUri] int key, Delta<Movie> patch)
17: {
18: Movie movieToPatch = _movies[key];
19: patch.Patch(movieToPatch);
20:return movieToPatch;
21: }
22: }
There’s a few things to point out here. Notice the [Queryable] attribute on the Get method. This enables OData query syntax on that particular action. So you can apply filtering, sorting, and other OData query options to the results of the action. Next, we have the [FromODataUri] attributes on the key parameters. These attributes instruct WebAPI that the parameters come from the URI and should be parsed as OData URI parameters instead of as WebAPI parameters. Finally, Delta<T> is a new OData class that makes it easy to perform partial updates on entities.
Instead of deriving from ODataController, you can also choose to derive from EntitySetController. EntitySetController is a convenient base class for exposing entity sets that provides simple methods you can override. It also takes care of sending back the right OData response in a variety of cases, like sending a 404 Not Found if an entity with a certain key could not be found. Here’s what the same implementation as above looks like with EntitySetController:
1:publicclass MoviesController : EntitySetController<Movie, int>
2: {
3: List<Movie> _movies = TestData.Movies;
4:
5: [Queryable]
6:publicoverride IQueryable<Movie> Get()
7: {
8:return _movies.AsQueryable();
9: }
10:
11:protectedoverride Movie GetEntityByKey(int key)
12: {
13:return _movies[key];
14: }
15:
16:protectedoverride Movie PatchEntity(int key, Delta<Movie> patch)
17: {
18: Movie movieToPatch = _movies[key];
19: patch.Patch(movieToPatch);
20:return movieToPatch;
21: }
22: }
Notice how you don’t need [FromODataUri] anymore because EntitySetController has already added it for you on its own action parameters. That’s just one of the several advantages of using EntitySetController as a base class.