Data Points

A New Option for Creating OData: Web API

Julie Lerman

Download the Code Sample ( VB Version)

Julie LermanMicrosoft .NET developers have been able to create OData feeds since before there was even an OData spec. By using WCF Data Services, you could expose an Entity Data Model (EDM) over the Web using Representational State Transfer (REST). In other words, you could consume these services through HTTP calls: GET, PUT, DELETE and so on. As the framework for creating these services evolved (and was renamed a few times along the way), the output evolved as well and eventually became encapsulated in the OData specification ( odata.org). Now there’s a great variety of client APIs for consuming OData—from .NET, from PHP, from JavaScript and from many other clients as well. But until recently, the only easy way to create a service was with WCF Data Services.

WCF Data Services is a .NET technology that simply wraps your EDM (.edmx, or a model defined via Code First) and then exposes that model for querying and updating through HTTP. Because the calls are URIs (such as http://mysite.com/mydataservice/­Clients(34)), you can even query from a Web browser or a tool such as Fiddler. To create a WCF Data Service, Visual Studio provides an item template for building a data service using a set of APIs.

Now there’s another way to create OData feeds—using an ASP.NET Web API. In this article I want to provide a high-level look at some of the differences between these two approaches and why you’d choose one over the other. I’ll also look at some of the ways creating an OData API differs from creating a Web API.

API vs. Data Service at a High Level

A WCF Data Service is a System.Data.Services.DataService that wraps around an ObjectContext or DbContext you’ve already defined. When you declare the service class, it’s a generic DataService of your context (that is, DataService<MyDbContext>). Because it starts out completely locked down, you set access permissions in the constructor to the DbSets from your context that you want the service to expose. That’s all you need to do. The underlying DataService API takes care of the rest: interacting directly with your context, and querying and updating the database in response to your client application’s HTTP requests to the service. It’s also possible to add some customizations to the service, overriding some of its query or update logic. But for the most part, the point is to let the DataService take care of most of the interaction with the context.

With a Web API, on the other hand, you define the context interaction in response to the HTTP requests (PUT, GET and the like). The API exposes methods and you define the logic of the methods. You don’t necessarily have to be interacting with Entity Framework or even a database. You could have in-memory objects that the client is requesting or sending. The access points won’t just be magically created like they are with the WCF Data Service; instead, you control what’s happening in response to those calls. This is the defining factor for choosing a service over an API to expose your OData. If most of what you want to expose is simple Create, Read, Update, Delete (CRUD) without the need for a lot of customization, then a data service will be your best choice. If you’ll need to customize a good deal of the behavior, a Web API makes more sense.

I like the way Microsoft Integration MVP Matt Milner put it at a recent gathering: “WCF Data Services is for when you’re starting with the data and model and just want to expose it. Web API is better when you’re starting with the API and want to define what it should expose.”

Setting the Stage with a Standard Web API

For those with limited experience with Web API, prior to looking at the new OData support I find it helpful to get a feel for the Web API basics and then see how they relate to creating a Web API that exposes OData. I’ll do that here—first creating a simple Web API that uses Entity Framework as its data layer, and then converting it to provide its results as OData.

One use for a Web API is as an alternative to a standard controller in a Model-View-Controller (MVC) application, and you can create it as part of an ASP.NET MVC 4 project. If you don’t want the front end, you can start with an empty ASP.NET Web application and add Web API controllers. However, for the sake of newbies, I’ll start with an ASP.NET MVC 4 template because that provides scaffolding that will spit out some starter code. Once you understand how all the pieces go together, starting with an empty project is the right way to go.

So I’ll create a new ASP.NET MVC 4 application and then, when prompted, choose the Empty template (not the Web API template, which is designed for a more robust app that uses views and is overkill for my purposes). This results in a project structured for an MVC app with empty folders for Models, Views and Controllers. Figure 1 compares the results of the Empty template to the Web API template. You can see that an Empty template results in a much simpler structure and all I need to do is delete a few folders.

ASP.NET MVC 4 Projects from Empty Template and Web API Template
Figure 1 ASP.NET MVC 4 Projects from Empty Template and Web API Template

I also won’t need the Models folder because I’m using an existing set of domain classes and a DbContext in separate projects to provide the model. I’ll then use the Visual Studio tooling to create my first controller, which will be a Web API controller to interact with my DbContext and domain classes that I’ve referenced from my MVC project. My model contains classes for Airline, Passengers, Flights and some additional types for airline-related data.

Because I used the Empty template, I’ll need to add some references in order to call into the DbContext—one to System.Data.Entity.dll and one to EntityFramework.dll. You can add both of these references by installing the EntityFramework NuGet package.

You can create a new Web API controller in the same manner as creating a standard MVC controller: right-click the Controllers folder in the solution and choose Add, then Controller. As you can see in Figure 2, you now have a template for creating an API controller with EF read and write actions. There’s also an Empty API controller. Let’s start with the EF read/write actions for a point of comparison to the controller we want for OData that will also use Entity Framework.

A Template for Creating an API Controller with Pre-Populated Actions
Figure 2 A Template for Creating an API Controller with Pre-Populated Actions

If you’ve created MVC controllers before, you’ll see that the resulting class is similar, but instead of a set of view-related action methods, such as Index, Add and Edit, this controller has a set of HTTP actions.

For example, there are two Get methods, as shown in Figure 3. The first, Get­Airlines, has a signature that takes no parameters and uses an instance of the AirlineContext (which the template scaffolding has named db) to return a set of Airline instances in an Enumerable. The other, GetAirline, takes an integer and uses that to find and return a particular airline.

Figure 3 Some of the Web API Controller Methods Created by the MVC Scaffolding

public class AirlineController : ApiController
  {
    private AirlineContext db = new AirlineContext2();
    // GET api/Airline
    public IEnumerable<Airline> GetAirlines()
    {
      return db.Airlines.AsEnumerable();
    }
    // GET api/Airline/5
    public Airline GetAirline(int id)
    {
      Airline airline = db.Airlines.Find(id);
      if (airline == null)
      {
        throw new HttpResponseException
          (Request.CreateResponse(HttpStatusCode.NotFound));
      }
      return airline;
    }

The template adds comments to demonstrate how you’d use these methods.

After providing some configurations to my Web API, I can check it out directly in a browser using the example syntax on the port my app has assigned: http://localhost:1702/api/Airline. This is the default HTTP GET call and is therefore routed by the application to execute the GetAirlines method. Web API uses content negotiation to determine how the result set should be formatted. I’m using Google Chrome as my default browser, which triggered the results to be formatted as XML. The request from the client controls the format of the results. Internet Explorer, for example, sends no specific header information with respect to what format it accepts, so Web API will default to returning JSON. My XML results are shown in Figure 4.

Figure 4 Airline WebAPI’s Response to GET, Displayed as XML in My Browser

<ArrayOfAirline xmlns:i=http://www.w3.org/2001/XMLSchema-instance 
  xmlns="http://schemas.datacontract.org/2004/07/DomainClasses">
    <Airline>
      <Id>1</Id>
      <Legs/>
      <ModifiedDate>2013-02-22T00:00:00</ModifiedDate>
      <Name>Vermont Balloon Transporters</Name>
    </Airline>
    <Airline>
      <Id>2</Id>
      <Legs/>
      <ModifiedDate>2013-02-22T00:00:00</ModifiedDate>
      <Name>Olympic Airways</Name>
    </Airline>
    <Airline>
      <Id>3</Id>
      <Legs/>
      <ModifiedDate>2013-02-22T00:00:00</ModifiedDate>
      <Name>Salt Lake Flyer</Name>
    </Airline>
</ArrayOfAirline>

If, following the guidance of the GetAirline method, I were to add an integer parameter to the request—http://localhost:1702/api/Airline/3—then only the single airline whose key (Id) is equal to 3 would be returned:

<Airline xmlns:i=http://www.w3.org/2001/XMLSchema-instance
  xmlns="http://schemas.datacontract.org/2004/07/DomainClasses">
    <Id>3</Id>
    <Legs/>
    <ModifiedDate>2013-02-22T00:00:00</ModifiedDate>
    <Name>Salt Lake Flyer</Name>
</Airline>

If I were to use Internet Explorer, or a tool such as Fiddler where I could explicitly control the request to the API to ensure I get JSON, the result of the request for Airline with the Id 3 would be returned as JSON:

{"Id":3,
  "Name":"Salt Lake Flyer",
  "Legs":[],
  "ModifiedDate":"2013-03-17T00:00:00"
}

These responses contain simple representations of the airline type with elements for each property: Id, Legs, ModifiedDate and Name.

The controller also contains a PutAirline method that Web API will call in response to a PUT HTTP request. PutAirline contains code for using the AirlineContext to update an airline. There’s also a PostAirline method for inserts and a DeleteAirline method for deleting. These can’t be demonstrated in a browser URL but you can find plenty of getting-started content for Web API on MSDN, Pluralsight and elsewhere, so I’ll move on to converting this to output its result according to the OData spec.

Turning Your Web API into an OData Provider

Now that you have a basic understanding of how Web API can be used to expose data using the Entity Framework, let’s look at the special use of Web API to create an OData provider from your data model. You can force your Web API to return data formatted as OData by turning your controller into an OData controller—using a class that’s available in the ASP.NET and Web Tools 2012.2 package—and then overriding its OData-specific methods. With this new type of controller, you won’t even need the methods that were created by the template. In fact, a more efficient path for creating an OData controller is to choose the Empty Web API scaffolding template rather than the one that created the CRUD operations. 

There are four steps I’ll need to perform for this transition:

  1. Make the controller a type of ODataController and implement its HTTP methods. I’ll use a shortcut for this.
  2. Define the available EntitySets in the project’s WebAPIConfig file.
  3. Configure the routing in WebAPIConfig.
  4. Pluralize the name of the controller class to align with OData conventions.

Creating an ODataController Rather than inherit from ODataController directly, I’ll use EntitySetController, which derives from ODataController and provides higher-level support by way of a number of virtual CRUD methods. I used NuGet to install the Microsoft ASP.NET Web API OData package for the proper assemblies that contain both of these controller classes.

Here’s the beginning of my class, now inheriting from EntitySetController and specifying that the controller is for the Airline type:

public class AirlinesController : EntitySetController<Airline,int>
{
  private AirlineContext db = new AirlineContext();
  public override IQueryable<Airline> Get()
  {
    return db.Airlines;
  }

I’ve fleshed out the override for the Get method, which will return db.Airlines. Notice that I’m not calling ToList or AsEnumerable on the Airlines DbSet. The Get method needs to return an IQueryable of Airline and that’s what db.Airlines does. This way, the consumer of the OData can define queries over this set, which will then get executed on the database, rather than pulling all of the Airlines into memory and then querying over them.

The HTTP methods you can override and add logic to are GET, POST (for inserts), PUT (for updates), PATCH (for merging updates) and DELETE. But for updates you’ll actually use the virtual method CreateEntity to override the logic called for a POST, the UpdateEntity for logic invoked with PUT and PatchEntity for logic needed for the PATCH HTTP call. Additional virtual methods that can be part of this OData provider are: CreateLink, DeleteLink and GetEntityByKey.

In WCF Data Services, you control which CRUD actions are allowed per EntitySet by configuring the SetEntitySetAccessRule. But with Web API, you simply add the methods you want to support and leave out the methods you don’t want consumers to access.

Specifying EntitySets for the API The Web API needs to know which EntitySets should be available to consumers. This confused me at first. I expected it to discover this by reading the AirlineContext. But as I thought about it more, I realized it’s similar to using the SetEntitySetAccessRule in WCF Data Services. In WCF Data Services, you define which CRUD operations are allowed at the same time you expose a particular set. But with the Web API, you start by modifying the WebApiConfig.Register method to specify which sets will be part of the API and then use the methods in the controller to expose the particular CRUD operations. You specify the sets using the ODataModelBuilder—similar to the DbContext.ModelBuilder you may have used with Code First. Here’s the code in the Register method of the WebApiConfig file to let my OData feed expose Airlines and Legs:

ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
                  modelBuilder.EntitySet<Airline>("Airlines");
                  modelBuilder.EntitySet<FlightLeg>("Legs");

Defining a Route to Find the OData Next, the Register method needs a route that points to this model so that when you call into the Web API, it will provide access to the EntitySets you defined:

Microsoft.Data.Edm.IEdmModel model = modelBuilder.GetEdmModel();
config.Routes.MapODataRoute("ODataRoute", "odata", model);

You’ll see that many demos use “odata” for the RoutePrefix parameter, which defines the URL prefix for your API methods. While this is a good standard, you can name it whatever you like.

So I’ll change it just to prove my point:

config.Routes.MapODataRoute("ODataRoute", "oairlinedata", model);

Renaming the Controller Class The application template generates code that uses a singular naming convention for controllers, such as AirlineController and LegController. However, the focus of OData is on the EntitySets, which are typically named using the plural form of the entity name. And because my EntitySets are indeed plural, I need to change the name of my controller class to AirlinesController to align with the Airlines EntitySet.

Consuming the OData

Now I can work with the API using familiar OData query syntax. I’ll start by requesting a listing of what’s available using the request: http://localhost:1702/oairlinedata/. The results are shown in Figure 5.

Figure 5. Requesting a List of Available Data

http://localhost:1702/oairlinedata/
<service xmlns="http://www.w3.org/2007/app" xmlns:atom=
  "http://www.w3.org/2005/Atom" 
  xml:base="http://localhost:1702/oairlinedata /">
    <workspace>
      <atom:title type="text">Default</atom:title>
      <collection href="Airlines">
        <atom:title type="text">Airlines</atom:title>
      </collection>
      <collection href="Legs">
        <atom:title type="text">Legs</atom:title>
      </collection>
    </workspace>
</service>

The results show me that the service exposes Airlines and Legs. Next, I’ll ask for a list of the Airlines as OData with http://localhost:1702/oairlinedata/Airlines. OData can be returned as XML or JSON. The default for Web API results is the JSON format:

{
  "odata.metadata":
    "http://localhost:1702/oairlinedata/$metadata#Airlines","value":[
    {
      "Id":1,"Name":"Vermont Balloons","ModifiedDate":"2013-02-26T00:00:00"
    },{
      "Id":2,"Name":"Olympic Airways","ModifiedDate":"2013-02-26T00:00:00"
    },{
      "Id":3,"Name":"Salt Lake Flyer","ModifiedDate":"2013-02-26T00:00:00"
    }
  ]
}

One of the many OData URI features is querying. By default, the Web API doesn’t enable querying, as that imposes an extra load on the server. So you won’t be able to use these querying features with your Web API until you add the Queryable annotation to the appropriate methods. For example, here I’ve added Queryable to the Get method:

[Queryable]
public override IQueryable<Airline> Get()
{
  return db.Airlines;
}

Now you can use the $filter, $inlinecount, $orderby, $sort and $top methods. Here’s a query using the OData filter method:

http://localhost:1702/oairlinedata/Airlines?$filter=startswith(Name,'Vermont')

The ODataController allows you to constrain the queries so that consumers don’t cause performance problems on your server. For example, you can limit the number of records that are returned in a single response. See the Web API-specific “OData Security Guidance” article at bit.ly/X0hyv3 for more details.

Just Scratching the Surface

I’ve looked at only a part of the querying capabilities you can provide with the Web API OData support. You can also use the virtual methods of the EntitySetController to allow updating to the database. An interesting addition to PUT, POST, and DELETE is PATCH, which lets you send an explicit and efficient request for an update when only a small number of fields have been changed, rather than sending the full entity for a POST. But the logic within your PATCH method needs to handle a proper update, which, if you’re using Entity Framework, most likely means retrieving the current object from the database and updating it with the new values. How you implement that logic depends on knowing at what point in the workflow you want to pay the price of pushing data over the wire. It’s also important to be aware that this release (with the ASP.NET and Web Tools 2012.2 package) supports only a subset of OData features. That means not all of the API calls you can make into an OData feed will work with an OData provider created with the Web API. The release notes for the ASP.NET and Web Tools 2012.2 package list which features are supported.

There’s a lot more to learn than I can share in the limited space of this column. I recommend Mike Wasson’s excellent series on OData in the official Web API documentation at bit.ly/14cfHIm. You’ll learn about building all of the CRUD methods, using PATCH, and even using annotations to limit what types of filtering are allowed in your OData APIs and working with relationships. Keep in mind that many of the other Web API features apply to the OData API, such as how to use authorization to limit who can access which operations. Also, the .NET Web Development and Tools Blog ( blogs.msdn.com/webdev) has a number of detailed blog posts about OData support in the Web API.


Julie Lermanis a Microsoft MVP, .NET mentor and consultant who lives in the hills of Vermont. You can find her presenting on data access and other Microsoft .NET topics at user groups and conferences around the world. She blogs at thedatafarm.com/blog and is the author of the “Programming Entity Framework” books from O’Reilly Media and numerous online courses at Pluralsight.com. Follow her on Twitter at twitter.com/julielerman.

Thanks to the following technical experts for reviewing this article: Jon Galloway (Microsoft) and Mike Wasson (Microsoft)
Jon Galloway ( Jon.Galloway@microsoft.com) is a Technical Evangelist on the Windows Azure evangelism team, focused on ASP.NET MVC and ASP.NET Web API. He speaks at conferences and international Web Camps from Istanbul to Bangalore to Buenos Aires. He's a co-author on the Wrox Professional ASP.NET MVC book series, and is a co-host on the Herding Code podcast.
Mike Wasson ( mwasson@microsoft.com) is a programmer-writer at Microsoft. For many years he documented the Win32 multimedia APIs. He currently writes about ASP.NET, focusing on Web API.

 

Rate: