如何启用web api以提供可使用$ select OData运算符查询的数据?
我正在使用Web API 2.2(或Microsort.AspNet.WebApi 5.2.2)我没有使用EF,我的后端是异步的,不支持IQueryable。我不介意在传递给客户端之前查询整个数据集并在Web服务器上进行过滤。
我下面的内容并不理想,因为它会返回Task<IQuerable...>
但是我不知道如何以另一种方式执行此操作,实际上根本不知道该怎么做。
以下代码有效,但在使用$ select时抛出错误(让我们称之为代码块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;
}
以下代码不起作用并返回406(我们称之为代码块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;
}
我猜后面的代码不起作用,因为它没有返回强类型对象,并且某种程度上序列化引擎无法处理这个问题。我试图这样做,我也尝试用Cars
替换代码块A中的dynamic
以启用$ select,但都不起作用。
所以有两个问题:
有没有办法启用$ select,考虑到我的后端不支持IQueryable? (无需深入了解IQueryable界面本身)
在不返回Task<IQuerable...>
的情况下执行此操作的“正确”方法是什么?
UPDATE - @Marvin Smit(错误 - 使用$ select时的代码块A)
尝试使用$ select投影代码块A时出现完整错误。示例/Cars?$select=NumberPlate
基本上它是序列化错误(与在任务中包装IQueryable有关)。设置断点表明数据已在queryOptions.ApplyTo(...
之后成功检索并投影,并且仅在返回后发生错误。
<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>
答案 0 :(得分:4)
问题与任务无关。
经过一番研究,问题是A区的这行代码:
queryOptions.ApplyTo(result.AsQueryable()) as IQueryable<Cars>
queryOptions.ApplyTo
会返回不属于IQueryable
类型的IQueryable<Cars>
,因此您无法投放它。结果为null,您最终会遇到此错误。似乎没有方法queryOptions.ApplyTo<T>
返回IQueryable<T>
。查看this和链接的网站,了解有关这两者的一些解释。
您实际上不需要应用查询选项来使odata过滤器工作。只需使用此代码即可。
[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();
return result.AsQueryable();
}
在方法返回后,asp.net odata实现会自动应用过滤器。
如果要在代码中应用查询选项,然后返回IQueryable (example here in scenario 9)以外的其他内容,则applyTo非常有用。
关于Block B中的序列化错误(在添加application / json之后)我不知道。
答案 1 :(得分:0)
我发现了这种方法:
[EnableQuery]
public async Task<IQueryable<Cars>> GetCars()
{
// return IQueryable<Cars> from context, do not perform request to database
return await Task.FromResult(_context.GetCarsQuery());
}