Getting started with OData in ASP.NET Core

November 06, 2017 by Anuraj

ASP.NET Core OData

This post is about getting started with OData in ASP.NET Core. OData (Open Data Protocol) is an ISO/IEC approved, OASIS standard that defines a set of best practices for building and consuming RESTful APIs. OData helps you focus on your business logic while building RESTful APIs without having to worry about the various approaches to define request and response headers, status codes, HTTP methods, URL conventions, media types, payload formats, query options, etc. OData also provides guidance for tracking changes, defining functions/actions for reusable procedures, and sending asynchronous/batch requests.

ASP.NET Core didn’t support OData officially. This blog post using a NuGet package, which looks like official, but it is not.

ASP.NET Core OData Package

First you need to create a Web API project with dotnet command. Once it is created, you need to add the OData package to your project, you can use the following command to do it.

dotnet add package Microsoft.AspNetCore.OData --version 1.1.0-alpha1

Next you need to create a model class and DBContext class. I am using an InMemory database. Here is the model class and DB Context class.

public class Person
{
    [Key]
    public int Id { get; set; }
    [Required]
    public string Name { get; set; }
    [Required]
    public int Age { get; set; }
}

public class SampleODataDbContext : DbContext
{
    public SampleODataDbContext(DbContextOptions options) : base(options)
    {
    }

    protected SampleODataDbContext()
    {
    }

    public DbSet<Person> Persons { get; set; }
}

Next you need to create a Controller, in earlier versions of OData you can inherit from ODataController. But in ASP.NET Core, there is no OData controller available. So you need to create a normal controller, with OData attributes.

public class PersonController : Controller
{
    private readonly AppDbContext _appDbContext;
    public PersonController(AppDbContext sampleODataDbContext)
    {
        _appDbContext = sampleODataDbContext;
    }

    [EnableQuery]
    public IActionResult Get()
    {
        return Ok(_appDbContext.Persons.AsQueryable());
    }
}

For EnableQuery attribute you require “Microsoft.AspNetCore.OData” namespace. Finally, you need to modify your startup class code to add OData middleware and OData routing.

public void ConfigureServices(IServiceCollection services)
{
    //Adding In Memory Database.
    services.AddDbContext<AppDbContext>(options =>
    {
        options.UseInMemoryDatabase("InMemoryDb");
    });
    //Adding OData middleware.
    services.AddOData();
    services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    //Adding Model class to OData
    var builder = new ODataConventionModelBuilder();
    builder.EntitySet<Person>(nameof(Person));
    //Enabling OData routing.
    app.UseMvc(routebuilder =>
    {
        routebuilder.MapODataRoute("odata", builder.GetEdmModel());
    });
}

Without the routing prefix - OData in this example, all the routes will be redirected to OData endpoints.

OData endpoint from Postman

And here is the output of $metadata endpoint.

OData Metadata endpoint

While developing the application, I faced three issues.

  • InvalidOperationException: Cannot resolve scoped service ‘Microsoft.OData.ODataSimplifiedOptions’ from root provider.

OData endpoint from Postman

I got this exception while working with application. I had to spent some time exploring this issue. And finally resolved it by adding following code in Main() method in program.cs

public class Program
{
    public static void Main(string[] args)
    {
        BuildWebHost(args).Run();
    }

    public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .UseDefaultServiceProvider(options =>
                options.ValidateScopes = false)
            .Build();
}
  • Some serialization exception while returning SingleResult from controller action -
[EnableQuery]
public SingleResult Get(int id)
{
    var result = _appDbContext.Persons.Where(p => p.Id == id);
    return SingleResult.Create(result);
}

Serialization exception - OData SingleResult

Unfortunately, I was not able to fix this exception. As a workaround, I did something like this.

[EnableQuery]
public IActionResult Get(int id)
{
    var result = _appDbContext.Persons.FirstOrDefault(p => p.Id == id);
    return Ok(result);
}
  • HTTP PUT was not working - Due to some strange issues, HTTP PUT was not working. It is always returning 404. I was not able to resolve this issue and I don’t have any workarounds for this.

Source code available on GitHub

Happy Programming :)

Copyright © 2024 Anuraj. Blog content licensed under the Creative Commons CC BY 2.5 | Unless otherwise stated or granted, code samples licensed under the MIT license. This is a personal blog. The opinions expressed here represent my own and not those of my employer. Powered by Jekyll. Hosted with ❤ by GitHub