Search code examples
odataasp.net-web-api2asp.net-apicontroller

How to Add OData $select functionality to WebAPI


I am using WebApi v5 and OData v4.

I have a controller which extends from ApiController with a method that polls a repository for a simple list of Categories.

public class ITSRController : ApiController
{
    private IITSRRepository repository;

    public ITSRController(IITSRRepository itsrRepository)
    {
        repository = itsrRepository;
    }

    [Route("v1/request/categories")]
    [HttpGet]
    public IQueryable<Category> Categories()
    {
        return repository.Categories.AsQueryable();
    }

The Category class is a simple repository Entity class:

public class Category
{
    public int Id { get; set; }
    public string CategoryName { get; set; }
}

My WebApiConfig has the following code in it:

public static void Register(HttpConfiguration config)
{
    config.MapHttpAttributeRoutes();
    config.AddODataQueryFilter();

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

Without any additional code, I get the expected results for GET requests like:

v1/request/categories/?$orderby=CategoryName
v1/request/categories/?$filter=CategoryName eq 'Cat1' or CategoryName eq 'Cat2'
v1/request/categories/?$top=4&$orderby=Id&$skip=4

When I make the following GET request: v1/request/categories/?$select=CategoryName

I get the following exception:

Object of type 'System.Linq.EnumerableQuery1[System.Web.Http.OData.Query.Expressions.SelectExpandBinder+SelectSome1[SWF.ITSR.Domain.Entities.Category]]' cannot be converted to type 'System.Collections.Generic.IEnumerable`1[SWF.ITSR.Domain.Entities.Category]'.

After some research I changed my Categories method in my controller:

[Route("v1/request/categories")]
[HttpGet]
[EnableQuery]
public IQueryable<Category> Categories(ODataQueryOptions options)
{
    if (options.SelectExpand != null)
    {
        Request.ODataProperties().SelectExpandClause = options.SelectExpand.SelectExpandClause;
    }

    return repository.Categories.AsQueryable();
}

When I call $select after this change I get the same error as above.

I've tried modifying the method to the following:

public IEnumerable<Category> Categories(ODataQueryOptions options)
{
    ...
    return repository.Categories.ToList();
}

and I get a slightly different exception when calling $select

Unable to cast object of type 'System.Linq.EnumerableQuery1[System.Web.Http.OData.Query.Expressions.SelectExpandBinder+SelectSome1[SWF.ITSR.Domain.Entities.Category]]' to type 'System.Collections.Generic.IEnumerable`1[SWF.ITSR.Domain.Entities.Category]'.

which may be semantically the same as the first error (perhaps failing somewhere else)

I spent a good many hours researching how to add $select (and $expand) functionality to my WebApi controller to no avail. I have tried countless posted suggestions, and still the answer eludes me. I would appreciate any help.

Thanks,

Ken


Solution

  • Is there a reason for you to use both Web API and Web API OData? If not, I suggest you Controller inherits directly from ODataController. Then you don't to specify anything particular to make $select work. An example is https://github.com/OData/ODataSamples/tree/master/SampleService