Channels ▼
RSS

Open Source

Using OData from ASP.NET


The generated controller code includes commented code that allows you to add a route for the controller. Change the code in App_Start/WebApiConfig.cs with the following lines that create an entity data model, also known as EDM, for the OData endpoint and add a route to this OData endpoint.

using System.Web.Http;
using System.Web.Http.OData.Builder;
using Games.Models;

namespace Games
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
            builder.EntitySet<Game>("Games");
            config.Routes.MapODataRoute("odata", "odata", builder.GetEdmModel());
        }
    }
}

The System.Web.Http.OData.Builder.ODataConventionModelBuilder class allows you to automatically map CLR classes to an EDM model based on a set of default naming conventions. You can have more control over the generating process by calling ODataModelBuilder methods to create the EDM. The call to the EntitySet method registers an entity set as part of the model. In this case, the name of the entity set is specified as Games in the single argument, and the conventions assume the controller is named GamesController. You can make additional calls to EntitySet to create the EMD model for each entity set you want to make available in a single endpoint.

Finally, the call to config.Routes.MapODataRoute maps the specified OData route and adds it to the OData endpoint. The first parameter specifies a friendly name for the route, and the second parameter sets the URI prefix for the endpoint. Thus, the URI for the Games entity set is /odata/Games. Obviously, it is necessary to add the hostname and port to the URI. For example, if the project URL is http://localhost:50723/, the URI for the Games entity set will be http://localhost:50723/odata/Games. The third parameter is the pre-built Microsoft.Data.Edm.IEdmModel that is retrieved from the builder with a call to the GetEdmModel method. In order to check the project URL, right-click on the project name (Games) in Solution Explorer, select Properties, click on Web, and read the configuration in the Project URL textbox.

Working with the OData Endpoint

You can press F5 to start debugging, and the OData endpoint will be ready to process requests. You can use either Telerik Fiddler or the curl utility to compose and send requests to the endpoint with different headers and check the results returned by the OData endpoint. Telerik Fiddler is a free Web debugging proxy with a GUI. The curl utility is a free and open source command line tool to transfer data to/from a server and it supports a large number of protocols. You can easily install curl in any Windows version from the Cygwin package installation option, and execute it from the Cygwin Terminal. I'll provide examples for both Fiddler and curl. I'll always use http://localhost:50723/ as my project URL in the samples (don't forget to replace it with your project URL in your own code).

If you send an HTTP GET request to http://localhost:50723/odata, you will receive the service document with the list of entity sets for the OData endpoint. In this case, the only entity set is Games. You can execute the following line in a Cygwin terminal to retrieve the service document with curl.

curl -X GET "http://localhost:50723/odata/"

The following lines show the raw HTTP response:

<?xml version="1.0" encoding="utf-8"?>
<service xml:base="http://localhost:50723/odata" xmlns="http://www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom">
  <workspace>
    <atom:title type="text">Default</atom:title>
    <collection href="Games">
      <atom:title type="text">Games</atom:title>
    </collection>
  </workspace>
</service>

In Fiddler, click Composer or press F9, select GET in the dropdown menu in the Parsed tab, and enter http://localhost:50723/odata/ in the textbox at the right-hand side of the dropdown (see Figure 1). Then, click Execute and double click on the 200 result that appears on the capture log. If you want to see the raw response, just click on the Raw button below the Request Headers panel (see Figure 2).

ASP.NET OData
Figure 1: Composing a request in Fiddler.

ASP.NET OData
Figure 2: Reading the raw response for the request in Fiddler.

If you add $metadata to the previous URI, the OData endpoint will return the service metadata document that describes the data model of the service with the Conceptual Schema Definition Language (CSDL) XML language. You can execute the following line in a Cygwin terminal to retrieve the service metadata document with curl. In Fiddler, you don't need to add a backslash (\) before the dollar sign ($); you can enter the raw URI: http://localhost:50723/odata/$metadata.

curl -X GET "http://localhost:50723/odata/\$metadata"

The following lines show the raw HTTP response:

<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="1.0" xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx">
  <edmx:DataServices m:DataServiceVersion="3.0" m:MaxDataServiceVersion="3.0" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
    <Schema Namespace="Games.Models" xmlns="http://schemas.microsoft.com/ado/2009/11/edm">
      <EntityType Name="Game">
        <Key>
          <PropertyRef Name="GameId" />
        </Key>
        <Property Name="GameId" Type="Edm.Int32" Nullable="false" />
        <Property Name="Name" Type="Edm.String" />
        <Property Name="Category" Type="Edm.String" />
        <Property Name="ReleaseYear" Type="Edm.Int32" Nullable="false" />
      </EntityType>
    </Schema>
    <Schema Namespace="Default" xmlns="http://schemas.microsoft.com/ado/2009/11/edm">
      <EntityContainer Name="Container" m:IsDefaultEntityContainer="true">
        <EntitySet Name="Games" EntityType="Games.Models.Game" />
      </EntityContainer>
    </Schema>
  </edmx:DataServices>
</edmx:Edmx>

Establish a breakpoint at the first line of the Post method within the GamesController class to see all the work done under the hood by ASP.NET Web API OData. Then, compose and execute an HTTP POST request to http://localhost:50723/odata/Games in Fiddler with the following values in the request headers and request body:

  • Request headers: Content-Type: application/json
  • Request body: { "Name":"Tetris 2014","Category":"Puzzle","ReleaseYear":2014 }

You can achieve the same effect with the following curl command:

curl -H "Content-Type: application/json" -d '{ "Name":"Tetris 2013","Category":"Puzzle","ReleaseYear":2013 }' -X POST "http://localhost:50723/odata/Games"

The request specifies that the body will use JSON and the Post method in C# receives a Game instance. This method works the same way as an ASP.NET MVC controller Post method, in which you check the ModelState.IsValid property value.

public async Task<IHttpActionResult> Post(Game game)

In this case, the model is valid, and when you continue with the execution, either Fiddler or curl will display the following HTTP 201 Created response with the URI for the recently generated element: http://localhost:50723/odata/Games(1):

HTTP/1.1 201 Created
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Location: http://localhost:50723/odata/Games(1)
Server: Microsoft-IIS/8.0
DataServiceVersion: 3.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?YzpcdXNlcnNcZ2FzdG9uXGRvY3VtZW50c1x2aXN1YWwgc3R1ZGlvIDIwMTNcUHJvamVjdHNcR2FtZXNcR2FtZXNcb2RhdGFcR2FtZXM=?=
X-Powered-By: ASP.NET
Date: Sat, 12 Jul 2014 04:09:44 GMT
Content-Length: 151

{
  "odata.metadata":"http://localhost:50723/odata/$metadata#Games/@Element","GameId":1,"Name":"Tetris 2014","Category":"Puzzle","ReleaseYear":2014
}

If you send an HTTP GET request to http://localhost:50723/odata/Games(1), you will receive the details for the recently saved element in the default JSON light serialization format. OData 3.0 introduced the JSON light serialization format to reduce footprint. The following two curl commands will produce the same result and will end in a call to the GetGame method with the key parameter that retrieves the Game from the database based on the received key value. (In Fiddler, you just need to add Accept: application/json to Request headers.)

curl -X GET "http://localhost:50723/odata/Games(1)"
curl -H "Accept: application/json" -X GET "http://localhost:50723/odata/Games(5)"

With or without the Accept: application/json line added to the request headers, the result will use the JSON light serialization format:

{
  "odata.metadata":"http://localhost:50723/odata/$metadata#Games/@Element","GameId":1,"Name":"Tetris 2014","Category":"Puzzle","ReleaseYear":2014
}

If you want to receive the response with the older OData v2 JSON "verbose" serialization format, you just need to add the Accept: application/json;odata=verbose line to the request headers. For example, the following curl command retrieves the same game with the verbose JSON format:

url -H "Accept: application/json;odata=verbose" -X GET "http://localhost:50723/odata/Games(1)"

The third option is to use the Atom Pub XML serialization format. If you want this format, you just need to add the Accept: application/atom+xml line to the request headers. The following curl command retrieves the same game with the Atom Pub XML format:

curl -H "Accept: application/json;odata=verbose" -X GET "http://localhost:50723/odata/Games(1)"

The GetGame method is decorated with the [EnableQuery] attribute; hence, you can use OData query parameters. For example, the following HTTP GET request uses the $select option to specify that it just wants the Name property to be included in the response body: http://localhost:50723/odata/Games(1)?$select=Name. In curl, you need to add a backslash (\) before the dollar sign ($):

curl -X GET "http://localhost:50723/odata/Games(1)?\$select=Name"

The following lines show the raw HTTP response that only includes the requested property value:

{
  "odata.metadata":"http://localhost:50723/odata/$metadata#Games/@Element&$select=Name","Name":"Tetris 2014"
}

The GetGames method is also decorated with the [EnableQuery] attribute. Thus, you can use the $filter query option to select the games that satisfy a specific predicate expression. For example, the following HTTP GET request uses the $filter option with the eq (equal) operator to retrieve the game whose name is equal to Tetris 2014:

http://localhost:50723/odata/Games?$filter=Name%20eq%20'Tetris%202014'

In curl, you need to add a backslash (\) before the dollar sign ($):

curl -X GET "http://localhost:50723/odata/Games?\$filter=Name%20eq%20'Tetris%202014'"

The following lines show the raw HTTP response:

{
  "odata.metadata":"http://localhost:50723/odata/$metadata#Games","value":[
    {
      "GameId":1,"Name":"Tetris 2014","Category":"Puzzle","ReleaseYear":2014
    }
  ]
}

The code for the GetGames method simply includes the [EnableQuery] attribute and is just one line that returns an IQueryable<Game>. As you can see, ASP.NET Web API OData performs a lot of work under the hood to parse and translate the OData query options and operators to a Linq query.

Conclusion

As you have learned from this simple example, ASP.NET Web API OData allows you to take advantage of your existing knowledge of both APS.NET MVC and the ASP.NET Web API to easily create OData endpoints. The good news is that you can customize all the components and easily add features, such as complex server-side behavior modification via the addition of OData actions. Enjoy!


Gastón Hillar is a senior contributing editor at Dr. Dobb's.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video