首页 > 解决方案 > OData v4 结果后处理(适用于 v3,不适用于 v4)

问题描述

我有一个 Web API 服务器(使用 EF 6.x),我需要对控制器中 OData 查询的结果集进行一些后处理。在客户端,我使用 DevEx 网格和他们的 ODataInstantFeedbackSource。

没有后处理,一切正常,例如:

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

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

它不适用于后处理(相同的简单 $count 查询,但没有 EnableQuery,因为我手动应用查询选项):

获取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());
}

这会引发异常:

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)

注意:使用 ODATA v3,上述工作正常。只有在 v4 中,不使用 [EnableQuery] 时才会出现异常。

如果我添加回 [EnableQuery] 属性,这个简单的 $count 查询适用于 ODATA v4,但对于更复杂的查询,返回给客户端的数据会变得混乱(可能是由于 $skip 等被我和通过 EnableQuery 属性)。

例如,向下滚动时 DevEx 网格生成的这个查询: http://somesite.us/odata/Items ?$orderby=ItemNo&$skip=300&$top=201

结果(客户端):意外的返回键数:0。预期:201

我假设我需要删除 EnableQuery 属性,因为我正在手动应用查询选项,但是为什么在执行此操作时会出现“无法转换为原始字符串”异常?

在这种情况下如何正确实施后处理?

标签: entity-frameworkasp.net-web-apiodata

解决方案


我就此向 Microsoft 提出了支持请求,他们最终确定这是 ODATA v4 中的一个错误,他们创建了这个错误报告: https ://github.com/OData/WebApi/issues/1586

解决方法是检查查询是否为计数查询,如果是,则返回 Ok(query.Count());

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

这是一个更完整的示例片段/POC,它适用于 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());
}

推荐阅读