如何在web api OData查询中启用$ select

时间:2015-08-13 11:30:25

标签: asp.net-web-api odata

如何启用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,但都不起作用。

所以有两个问题:

  1. 有没有办法启用$ select,考虑到我的后端不支持IQueryable? (无需深入了解IQueryable界面本身)

  2. 在不返回Task<IQuerable...>的情况下执行此操作的“正确”方法是什么?

  3. 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>
    

2 个答案:

答案 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());
}