How do you enable web api to provide data that is queryable with the $select OData operator?
I'm using Web API 2.2 (or Microsort.AspNet.WebApi 5.2.2) I'm not using EF and my backend is asynchronous and doesn't support IQueryable. I don't mind querying the entire data set and filtering on the web-server before passing off to the client.
What I have below isn't ideal because it returns a Task<IQuerable...>
however I don't know how to do it another way, and in fact don't know how to do it at all.
The following code works, however throws an error when using $select (let's call it code block A):
[EnableQuery(HandleNullPropagation = HandleNullPropagationOption.True)]
public async Task<IQueryable<Cars>> GetCars(ODataQueryOptions<Cars> queryOptions)
{
// validate the query.
try
{
queryOptions.Validate(_validationSettings);
}
catch (ODataException ex)
{
throw new HttpRequestException(ex.Message);
}
var result = await _context.GetCarsAsync();
var queryableResult = queryOptions.ApplyTo(result.AsQueryable()) as IQueryable<Cars>;
return queryableResult;
}
The following code doesn't work and returns a 406 (let's call it code block B):
[EnableQuery(HandleNullPropagation = HandleNullPropagationOption.True)]
public async Task<IQueryable> GetCars(ODataQueryOptions<Cars> queryOptions)
{
// validate the query.
try
{
queryOptions.Validate(_validationSettings);
}
catch (ODataException ex)
{
throw new HttpRequestException(ex.Message);
}
var result = await _context.GetCarsAsync();
var queryableResult = queryOptions.ApplyTo(result.AsQueryable());
return queryableResult;
}
I guess the latter code doesn't work because it isn't returning a strongly typed object and somehow the serialization engine can't handle this. I was trying to do this and I also tried replacing Cars
in code block A with dynamic
to enable $select, but neither work.
So two questions:
Is there any way to enable $select, considering my backend doesn't support IQueryable? (Without digging into the IQueryable interface itself)
What is the 'correct' way to do this without returning a
Task<IQuerable...>
?
UPDATE - @Marvin Smit (Error - Code Block A when using $select)
The full error when trying to project the code block A using $select is below. Example /Cars?$select=NumberPlate
Basically it's a serialization error (something to do with wrapping IQueryable in a Task?). Putting a break point in shows that the data has successfully been retrieved and projected after the queryOptions.ApplyTo(...
and the error only occurs after returning.
<m:error xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<m:code/>
<m:message xml:lang="en-US">An error has occurred.</m:message>
<m:innererror>
<m:message>
The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'.
</m:message>
<m:type>System.InvalidOperationException</m:type>
<m:stacktrace/>
<m:internalexception>
<m:message>Cannot serialize a null 'feed'.</m:message>
<m:type>
System.Runtime.Serialization.SerializationException
</m:type>
<m:stacktrace>
at System.Web.Http.OData.Formatter.Serialization.ODataFeedSerializer.WriteObjectInline(Object graph, IEdmTypeReference expectedType, ODataWriter writer, ODataSerializerContext writeContext)
at System.Web.Http.OData.Formatter.Serialization.ODataFeedSerializer.WriteObject(Object graph, Type type, ODataMessageWriter messageWriter, ODataSerializerContext writeContext)
at System.Web.Http.OData.Formatter.ODataMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content, HttpContentHeaders contentHeaders)
at System.Web.Http.OData.Formatter.ODataMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at System.Web.Http.WebHost.HttpControllerHandler.
<WriteBufferedResponseContentAsync>d__1b.MoveNext()
</m:stacktrace>
</m:internalexception>
</m:innererror>
</m:error>