2
votes

I have a Web API server (with EF 6.x) and I need to do some post-processing of the result set from OData queries in the controller. On the client-side I use a DevEx grid and their ODataInstantFeedbackSource.

With no post-processing, everything works fine, e.g.:

http://somesite.us/odata/Items/$count

[EnableQuery]
public IHttpActionResult GetItems(ODataQueryOptions<Item> queryOptions)
{
    return Ok(Context.Items);
}

It does not work with post-processing (same simple $count query, but without EnableQuery since I am manually applying the query options):

GET http://somesite.us/odata/Items/$count

//[EnableQuery]
public IHttpActionResult GetItems(ODataQueryOptions<Item> queryOptions)
{
    queryOptions.Validate(_validationSettings);
    var query = queryOptions.ApplyTo(Context.Items, new ODataQuerySettings()) as IQueryable<Item>;
    var resultList = new List<Item>();
    foreach (var item in query)
    {
        item.OrdStat = "asf"; // Some post-processing
        resultList.Add(item);
    }
    return Ok(resultList.AsQueryable());
}

This throws an exception:

Microsoft.OData.ODataException
  HResult=0x80131509
  Message=The value of type 'System.Linq.EnumerableQuery`1[[SomeService.Model.Item, SomeService.Model, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' could not be converted to a raw string.
  Source=Microsoft.OData.Core
  StackTrace:
   at Microsoft.OData.RawValueWriter.WriteRawValue(Object value)

Note: with ODATA v3, the above works just fine. It is only with v4 that I get an exception when not using [EnableQuery].

If I add back the [EnableQuery] attribute, this simple $count query works with ODATA v4, but with more complex queries, the data returned to the client gets messed up (likely due to $skip, etc. being applied both by me and by the EnableQuery attribute).

For example, this query generated by the DevEx grid when you scroll down: http://somesite.us/odata/Items?$orderby=ItemNo&$skip=300&$top=201

Results in (client-side): Unexpected number of returned keys: 0. Expected: 201

I assume that I need to remove the EnableQuery attribute since I am manually applying the query options, but why am I getting the "could not be converted to a raw string" exception when I do this?

How can I properly implement post-processing in this scenario?

1

1 Answers

0
votes

I opened a support request with Microsoft on this, and they eventually determined that it is a bug in ODATA v4 and they created this bug report: https://github.com/OData/WebApi/issues/1586

The workaround is to check if the query is a count query and, if so, return Ok(query.Count());

if (queryOptions.Context.Path?.Segments.LastOrDefault() is CountSegment)
    return Ok(query?.Count());

Here is a more complete sample snippet / POC which works fine with ODATA v4:

private static ODataValidationSettings _validationSettings = new ODataValidationSettings();

[ODataRoute("Customers")]
public IHttpActionResult Get(ODataQueryOptions<CustomerLookup> queryOptions)
{
    queryOptions.Validate(_validationSettings);
    var query = queryOptions.ApplyTo(Context.CustomerLookup) as IQueryable<CustomerLookup>;
    if (queryOptions.Context.Path?.Segments.LastOrDefault() is CountSegment)
        return Ok(query?.Count());

    var resultList = new List<CustomerLookup>();
    foreach (var customer in query)
    {
        customer.Address = "1234_" + customer.Address;
        resultList.Add(customer);
    }

    return Ok(resultList.AsQueryable());
}