9

I'm building an OData 3 service on Web API 2.2.

The service is correctly returning the metadata for my entities, but returns 406 Not Available when I query one of the actual entities. I've done quite a bit of research (I'm currently following several tutorials), but I haven't found anything that actually works.

Here's my WebApiConfig:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using System.Web.OData.Builder;
using System.Web.OData.Extensions;

namespace MyProject
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            ODataModelBuilder builder = new ODataConventionModelBuilder();

            builder.EntitySet<MarvelCharacter>("MarvelCharacters");
            config.MapODataServiceRoute(
                routeName: "Marvel",
                routePrefix: "dude",
                model: builder.GetEdmModel());
        }
    }
}

And my controller (not complete, but you get the idea):

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.OData;
using System.Web.Http.OData.Query;
using Microsoft.Data.OData;
using MyProject;

namespace MyProject.Controllers
{
    public class MarvelCharactersController : ODataController
    {
        private static ODataValidationSettings _validationSettings = new ODataValidationSettings();

        // GET: odata/MarvelCharacters
        public IHttpActionResult GetMarvelCharacters(ODataQueryOptions<MarvelCharacter> queryOptions)
        {
            // validate the query.
            try
            {
                queryOptions.Validate(_validationSettings);
            }
            catch (ODataException ex)
            {
                return BadRequest(ex.Message);
            }

            var entities = new myEntities();
            var marvelCharacters = (from c in entities.MarvelCharacters select c).ToList();

            return Ok<IEnumerable<MarvelCharacter>>(marvelCharacters);
        }
    }
}
Luke Puplett
  • 42,091
  • 47
  • 181
  • 266
JDB
  • 25,172
  • 5
  • 72
  • 123

1 Answers1

27

Turns out the answer to this one was pretty simple, but not covered well by any documentation I could find.

I was trying to implement an OData 3 endpoint on WebAPI 2.2. I was following several different tutorials, some for OData 3 and some for OData 4.

I was using OData 4 (System.Web.OData) in my WebApiConfig and OData 3 (System.Web.Http.OData) in my controller. Turns out, they don't play well together.

I decided to post the answer here in case anyone else has a similar issue.

To add a little value, and since I was mixing both anyway, I decided to go ahead and setup support for both version 3 and 4 by aliasing the namespaces in my WebApiConfig and then creating versioned controllers.

WebApiConfig:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using ODataV3 = System.Web.Http.OData;
using ODataV4 = System.Web.OData;

namespace MyProject
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // OData V3 Route

            ODataV3.Builder.ODataModelBuilder builder3 = new ODataV3.Builder.ODataConventionModelBuilder();

            builder3.EntitySet<MarvelCharacter>("MarvelCharactersV3");
            // The MapODataRoute function is deprecated in WebAPI 2.2,
            // but I haven't found an alternative for supporting OData 3.
            config.Routes.MapODataRoute(
                routeName: "Marvel3",
                routePrefix: "dude3",
                model: builder3.GetEdmModel());

            // ODate V4 Route

            ODataV4.Builder.ODataModelBuilder builder4 = new ODataV4.Builder.ODataConventionModelBuilder();

            builder4.EntitySet<MarvelCharacter>("MarvelCharactersV4");
            ODataV4.Extensions.HttpConfigurationExtensions.MapODataServiceRoute(
                configuration: config,
                routeName: "Marvel4",
                routePrefix: "dude4",
                model: builder4.GetEdmModel());
        }
    }
}

MarvelCharactersV3 (OData 3):

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.OData; // OData V3
using System.Web.Http.OData.Query;
using Microsoft.Data.OData;
using MyProject;

namespace MyProject.Controllers
{
    public class MarvelCharactersV3Controller : ODataController
    {
        private static ODataValidationSettings _validationSettings = new ODataValidationSettings();

        // GET: odata/MarvelCharacters
        public IHttpActionResult GetMarvelCharactersV3(ODataQueryOptions<MarvelCharacter> queryOptions)
        {
            // validate the query.
            try
            {
                queryOptions.Validate(_validationSettings);
            }
            catch (ODataException ex)
            {
                return BadRequest(ex.Message);
            }

            var entities = new myEntities();
            var marvelCharacters = (from c in entities.MarvelCharacters select c).ToList();

            return Ok<IEnumerable<MarvelCharacter>>(marvelCharacters);
        }
    }
}

MarvelCharactersV4 (OData 4):

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.OData; // OData 4
using System.Web.OData.Query;
using Microsoft.Data.OData;
using MyProject;

namespace MyProject.Controllers
{
    public class MarvelCharactersV4Controller : ODataController
    {
        private static ODataValidationSettings _validationSettings = new ODataValidationSettings();

        // GET: odata/MarvelCharacters
        public IHttpActionResult GetMarvelCharactersV4(ODataQueryOptions<MarvelCharacter> queryOptions)
        {
            // validate the query.
            try
            {
                queryOptions.Validate(_validationSettings);
            }
            catch (ODataException ex)
            {
                return BadRequest(ex.Message);
            }

            var entities = new myEntities();
            var marvelCharacters = (from c in entities.MarvelCharacters select c).ToList();

            return Ok<IEnumerable<MarvelCharacter>>(marvelCharacters);
        }
    }
}

It's probably not the best architecture (I will probably create a library to consolidate similar code between the controllers), but I've tested and I can successfully query via OData 3 and OData 4, so I'm happy enough with it for now.

JDB
  • 25,172
  • 5
  • 72
  • 123
  • 9
    I'm kinda wishing Microsoft had actually used 'OdataV3' and 'OdataV4' namepaces instead of tucking new versions here and there willy nilly +1 for the alias - great idea – bkwdesign Aug 13 '15 at 20:17
  • I am having same issue, first setup with oData v3 and later moved to oData v4, and controllers created with V4 started giving 406 error. Created one controller again with V3 to verify the problem and yes its starts working. Now upgrading to V4 for all. – Zafar Dec 20 '16 at 02:18
  • 1
    Thank you so much! – Jonathan Quinth Mar 07 '17 at 09:26
  • Massive thank you JDB. The name space allocations by Microsoft are nutty and badly documented. – Andy Horton Aug 22 '18 at 10:51