Getting started with Web API and OData V4 Part 1

This article demonstrates how to create an OData V4 Web API service which uses Entity Framework 6 and SQL 2014 for persistence. OData V4 was released with Web API 2.2. The OData V4 standard is documented here: http://www.odata.org/documentation/odata-version-4-0/. The Web API implementation does supports a lot of features from the standard, but not all.

Part 1 Getting started with Web API and OData V4 Part 1
Part 2 Web API and OData V4 Queries, Functions and Attribute Routing Part 2
Part 3 Web API and OData V4 CRUD and Actions Part 3
Part 4 Web API OData V4 Using enum with Functions and Entities Part 4
Part 5 Web API OData V4 Using Unity IoC, SQLite with Ef6 and OData Model Aliasing Part 5
Part 6 Web API OData V4 Using Contained Models Part 6
Part 7 Web API OData V4 Using a Singleton Part 7
Part 8 Web API OData V4 Using an OData T4 generated client Part 8
Part 9 Web API OData V4 Caching Part 9
Part 10 Web API OData V4 Batching Part 10
Part 11 Web API OData V4 Keys, Composite Keys and Functions Part 11

Code: https://github.com/damienbod/WebAPIODataV4

For this example, an IIS hosted web application is used. Create a new MVC project with Web API. Once the project has been created, open the NuGet packages and update all packages in the project.

The AdventureWorks2012 database is used for the SQL database. Download the database here:
http://msftdbprodsamples.codeplex.com/releases/view/55330

Then attach this file to your Microsoft SQL server 2014. (Microsoft SQL Server 2012 could also be used.) Once the database is attached and running, you can create an entity model from the database.

In the Visual Studio project, create a new folder. Click Add a new item. Choose the data menu and add ADO.NET Entity Data Model:
WebAppODataV4_01

Add and test your connection string. Now choose Code First from Database (The database already exists).
WebAppODataV4_02

Select all the tables from the Person schema.
WebAppODataV4_03

Now all entities from the Person schema will be added to the DomainModel.
WebAppODataV4_04

Next step is to add an OData Controller. Add the Web API ODATA V4 packages from NuGet Microsoft.AspNet.OData. (Be careful not to use the old V3 packages.)

odatav4WebApi_05

Now create a PersonController which inherits from the ODataController class.

using System.Linq;
using System.Runtime.Serialization;
using System.Web.Http;
using System.Web.OData;
using WebAppODataV4.Database;

namespace WebAppODataV4.Controllers
{
    public class PersonController : ODataController
    {
        readonly DomainModel _db = new DomainModel();

        [EnableQuery(PageSize = 20)]
        public IHttpActionResult Get()
        {  
            return Ok(_db.Person.AsQueryable());
        }

        public IHttpActionResult Get([FromODataUri] int key)
        {
            return Ok(_db.Person.SingleOrDefault(t => t.BusinessEntityID == key));
        }

        protected override void Dispose(bool disposing)
        {
            _db.Dispose();
            base.Dispose(disposing);
        }
    }
}

Add the OData model and route configuration to the Web API config. All entities from the person schema are added. These will be used in later posts. Only the Person Entity is required for the PersonController. The OData configuration has been removed to its own method. The MapODataServiceRoute method is now used in OData V4. This is different to OData V3.

using System.Linq;
using System.Web.Http;
using System.Web.OData.Builder;
using System.Web.OData.Extensions;
using Microsoft.Data.Edm;
using WebAppODataV4.Database;

namespace WebAppODataV4
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            config.MapODataServiceRoute("odata", "odata", model: GetModel());         
        }

        public static Microsoft.OData.Edm.IEdmModel GetModel()
        {
            ODataModelBuilder builder = new ODataConventionModelBuilder();

            builder.EntitySet<Address>("Address");
            builder.EntitySet<AddressType>("AddressType");
            builder.EntitySet<BusinessEntity>("BusinessEntity");
            builder.EntitySet<BusinessEntityAddress>("BusinessEntityAddress");
            builder.EntitySet<BusinessEntityContact>("BusinessEntityContact");
            builder.EntitySet<ContactType>("ContactType");
            builder.EntitySet<CountryRegion>("CountryRegion");
            builder.EntitySet<EmailAddress>("EmailAddress");
            builder.EntitySet<Password>("Password");
            builder.EntitySet<Person>("Person");
            builder.EntitySet<PersonPhone>("PersonPhone");
            builder.EntitySet<PhoneNumberType>("PhoneNumberType");
            builder.EntitySet<StateProvince>("StateProvince");

            EntitySetConfiguration<Person> persons = builder.EntitySet<Person>("Person");

            FunctionConfiguration myFirstFunction = persons.EntityType.Collection.Function("MyFirstFunction");
            myFirstFunction.ReturnsCollectionFromEntitySet<Person>("Person");
          
            return builder.GetEdmModel();
        }
    }
}

Now the OData service can be used.

http://localhost:51902/odata/Person?$select=FirstName,MiddleName,LastName
http://localhost:51902/odata?$metadata

These links can be tested with Fiddler or Postman
WebAppODataV4_06
Should your controller inherit from EntitySetController, ODataController or ApiController?
It is recommended that you use the ODataController for OData requests. You have more control.

Now that the OData V4 service is up and running, in the following posts, we want to explore some of the new features and also features which existed in V3 as well.

The SAMPLES link is a great resource for Web API 2.2 and ODATA V4.

Important note:
The new OData V4 libraries are in the assembly: System.Web.OData. System.Web.Http.OData are the V3 libraries.

Links:

SAMPLES: https://aspnet.codeplex.com/SourceControl/latest#Samples/WebApi/OData/v4/

http://aspnetwebstack.codeplex.com/

http://www.asp.net/web-api/overview/releases/whats-new-in-aspnet-web-api-22

http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-routing-conventions

http://stackoverflow.com/questions/18233059/apicontroller-vs-odatacontroller-when-exposing-dtos

17 comments

  1. Cool post! That’s a technical in deep description of what I suggest to solve legacy scenarios in my last post: http://robinsedlaczek.wordpress.com/2014/06/06/you-can-escape-from-legacy-code-dare-to-start-changing-things/

    1. Thanks, and also thanks for the link
      greetings Damien

      1. Nice to help you! Thanks for the comment!🙂

  2. A very, very useful technical blog – thanks a lot!

    One thing that I absolutely NOT understand is the way how “ODataRoutingAttribute” is working. How do you use it in comparison to the “normal” RoutingAttribute in ApiControllers? Are these “ODataRoute” Attributes only valid in combination with actions and functions?
    What I want to do is the following: have a controller with two different methods and give all two methods a different route (like I can do in ApiController). Example:

    [ODataRoutePrefix(“companies”)]
    public class CompanyController: ODataController
    {
    [HttpGet]
    [ODataRoute(“routeone”)]
    public IHttpActionResult GetOneMethod()
    {
    ….;
    return ok(…);
    }

    [HttpGet]
    [ODataRoute(“routetwo”)]
    public IHttpActionResult GetTwoMethod()
    {
    ….;
    return ok(…);
    }
    }

    This is not working for me. If I call the metadata I get the error that the path template for ‘companies/routeone’ is not a valid OData path template. I think I am understanding something completely wrong!

    BTW: there are suddenly lots of “&amp;amp;…” in your source code examples. When I took a first look a few days ago I swear that these were not there.

  3. Thanks, I have fixed all the code examples… The OData Model requires a function or an action called routeone or routetwo on the EntityType Collection companies. Then the routes will work. You need to add these to the OData Model.

    greetings Damien

  4. Ah, ok, now I understand this route-stuff. In my opinion not a very intuitive way of declaring routes by doing it twice in two different locations.
    Thanks for fixing the code examples, now much more readable.🙂

  5. Reblogged this on josephdung.

  6. Reblogged this on denhul.

  7. davidbf · · Reply

    This is platinum, thanks a lot man.

  8. When I walk through the above steps, the $metadata enpoint works as expected, but when I try the odata//Product endpoint I get a 404 not found. And when I try api/Product I get a 406 Not Acceptable response. What have I missed?

    1. Turns out I had some lingering references to System.Web.Http.OData. Replaced them all with System.Web.OData and the 406 errors went away.

      1. Rafael · ·

        I have equal situation but dont having reference to System.Web.Http.OData.
        Errors 406 persist

  9. Hi Damien,
    fabulous tutorial thank you. I was wondering where exactly is the Controller called by the service?
    The reason I ask is that my controller is StewController but odata/STEW(1) is what works, for example.
    ta
    Stew

  10. when i want to add a new table of AdventureWorks2012 into DataBase folder, is it the only way to delete all the classes in the DataBase folder in your demo project and then add a new entity data model?

  11. for example ,the article above you select all the tables from the Person schema, but if I want to select all tables from person schema and production schema, i can only delete all classes in Database folder and then regenerate a new edm?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: