Search code examples
odataasp.net-web-apiasp.net-web-api2odata-v4

Web API 2.2 - OData v4 ODataQueryOptions.applyTo() returning null when $select or $expand present?


I have setup a Web API 2.2 ODataController such that I can process my OData options manually (i.e. Without [EnableQuery] and or using a ODataQueryOptions parameter). However I have run into what looks like a bug. Given the following code:

public IHttpActionResult GetEmployees() {
//Get Queryable Item (in this case just an in-memory List made queryable)
  IQueryable<Employee> employees = _Employees.AsQueryable();

//Get Requested URI without querystring (there may be other ways of doing this)
  String newUri = Request.RequestUri.AbsoluteUri;
  if (!String.IsNullOrEmpty(Request.RequestUri.Query)) {
    newUri = newUri.Replace(Request.RequestUri.Query, "");
  }

//Add custom OData querystring (this is for example purposes)
  newUri = String.Format("{0}?$skip={1}&$top={2}", newUri, 1, 1);

//Create new HttpRequestMessage from the updated URI
  HttpRequestMessage newRequest = new HttpRequestMessage(Request.Method, newUri);

//Create new ODataQueryContext based off initial request (required to create ODataQueryOptions)
  ODataQueryContext newContext = new ODataQueryContext(Request.ODataProperties().Model, typeof(Employee), Request.ODataProperties().Path);

//Create new ODataQueryOptions based off new context and new request
  ODataQueryOptions<Employee> newOptions = new ODataQueryOptions<Employee>(newContext, newRequest);

//Apply the new ODataQueryOptions to the Queryable Item
  employees = newOptions.ApplyTo(employees) as IQueryable<Employee>;

//Return List (will be serialized by OData formatter)
  return Ok(employees.ToList());
}

Which works 100%, however adding a $select or $expand like this:

newUri = String.Format("{0}?$skip={1}&$top={2}&$expand=Projects", newUri, 1, 1);

will return null from

employees = newOptions.ApplyTo(employees) as IQueryable<Employee>;

which has forced me to create two separate ODataQueryOptions, one to applyTo the IQueryable (without any $select or $expand), and another with just the $selects/$expands to build the SelectExpandClause to assign to Request.ODataProperties().SelectExpandClause.

I just don't understand why the null return is happening. The core intent behind this code is to allow for better control over processing OData when working with ORMs other than Entity Framework. So realistically I'd end up overriding applyTo anyways (or just manually processing the expression tree myself), but this particular example still seems like a bug to me.

Can anyone give me some insight on this? Maybe there is just something I'm missing.


Solution

  • (Moving my comment above to an answer)

    Because what you're really getting from ApplyTo (once you add $select or $expand) is

    System.Web.OData.Query.Expressions.SelectExpandBinder.SelectAllAndExpand<Employe‌​e>
    

    which, of course, can't be cast to IQueryable, hence the null.

    Why not add the EnableQuery attribute, and return an IQueryable (instead of ToList)?