Search code examples
asp.netentity-frameworkasync-awaitodata

OData web api - upgrade to AsyncEntitySetController - Get method


We have WEB API which uses OData. Our controllers inherit from EntitySetController and now we are changing it to AsyncEntitySetController

Patch, Post, Put, Get(key) methods are easy to change, but the problem is with Get method.

In EntitySetController it returns IQueryable, filters specified in URI are further applied and then query is executed.

But in AsyncEntitySetController it returns IEnumerable so we need to call ToListAsync in method and I am affraid that parameters from URI are not applied or query is called before the filters are applied and filters are applied to memory collection.

How to achieve that it behaves the same way as with EntitySetController, so:

  • iqueryable is made
  • filters from URI are applied
  • query is called in async/await way?

Solution

  • I managed to solve it by using QueryOptions.ApplyTo(query). So my previous code in controller class derived from EntitySetController:

    public override IQueryable<Entity> Get()
    {
        return this.DataContext.Entities;
    }
    

    was transformed into controller class derived from AsyncEntitySetController, like this:

    public override Task<IEnumerable<Entity>> Get()
    {
        var query = this.DataContext.Entities;
    
        query = (IQueryable<Entity>)this.QueryOptions.ApplyTo(query);
        return query.ToListAsync();
    }
    

    One note to mention. Skip from query parameters in URI gets applied twice, see: WebAPI OData $Skip on custom IQueryable double applied

    You can implement your own EnableQuery attribute like this and decorate Get method with that in controller:

    public sealed class EnableQueryIgnoreSkipAttribute : EnableQueryAttribute
    {
        public override IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions)
        {
            // Do not apply Skip
            var skipOption = new SkipQueryOption("0", queryOptions.Context);
            typeof(ODataQueryOptions).GetProperty(nameof(ODataQueryOptions.Skip)).SetValue(queryOptions, skipOption, null);
    
            return base.ApplyQuery(queryable, queryOptions);
        }
    }
    

    And decorate method like this:

    [EnableQueryIgnoreSkip]
    public override Task<IEnumerable<Entity>> Get()