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: 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


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:

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:

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

Select all the tables from the Person schema.

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

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.)


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)

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)

                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();


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

            FunctionConfiguration myFirstFunction = persons.EntityType.Collection.Function("MyFirstFunction");
            return builder.GetEdmModel();

Now the OData service can be used.


These links can be tested with Fiddler or Postman
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.




  1. Cool post! That’s a technical in deep description of what I suggest to solve legacy scenarios in my last post:

    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:

    public class CompanyController: ODataController
    public IHttpActionResult GetOneMethod()
    return ok(…);

    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.

  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?

  12. Hi Damien,

    Thank you for the article.

    I have one query with entity model having composite key, I want to do PATCH with composite key.

    Could you please suggest me.

  13. Kari Sergeant   · · Reply

    Good tidings, Generally I never remark on online journals yet your article is persuading to the point that I never stop myself to say something regarding it. You’re working effectively, Keep it up. You can look at this article, may be of help 🙂

Leave a Reply to dineshramitc Cancel reply

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

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

Facebook photo

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

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: