4

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.

Xorcist
  • 3,129
  • 3
  • 21
  • 47
  • 2
    Because what you're really getting from ApplyTo (once you add $select or $expand) is System.Web.OData.Query.Expressions.SelectExpandBinder.SelectAllAndExpand which, of course, can't be cast to IQueryable, hence the null. I myself have been trying to unwrap that structure (I can see an "Instance" property buried down inside it), so I'm very interested if you (or someone else) has/finds a solution... – Michael Apr 30 '15 at 01:28

1 Answers1

6

(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)?

Michael
  • 4,010
  • 4
  • 28
  • 49
  • 1
    I was trying figure out how to process the OData expression tree manually, as I wanted to NHibernate as my ORM instead of Entity Framework. And with EnableQuery, items like $skip and $top would get processed twice, once manually by the NHibernate routine, and a second time automatically further down the pipeline when OData processed the object (apparently as an IQueryable). So I had to ditch the EnableQuery attribute. I just found it odd that the return type on that routine changed give a different input. It was confusing. – Xorcist May 29 '15 at 18:57