WebAPI OData $跳过自定义IQueryable double应用

时间:2015-06-03 00:27:55

标签: c# asp.net-web-api odata iqueryable

我实现了一个通过WebAPI OData端点公开的自定义IQueryable。控制器的Get()结构相当标准:

[EnableQuery(
    AllowedQueryOptions = AllowedQueryOptions.Count
                          | AllowedQueryOptions.Filter
                          | AllowedQueryOptions.OrderBy
                          | AllowedQueryOptions.Skip
                          | AllowedQueryOptions.Top)]
[ODataRoute]
public PageResult<Foo> Get(ODataQueryOptions<Foo> queryOptions)
{

    var bars = new QueryableData<Foo>(_provider);

    var result = ((IQueryable<Foo>)queryOptions
        .ApplyTo(bars,
            new ODataQuerySettings(new ODataQuerySettings { EnableConstantParameterization = false, EnsureStableOrdering = false }))).ToList();
    var count = _provider.Count;
    return new PageResult<Foo>(result, null, count);
}

我看到的奇怪行为是,在返回PageResult后应用查询字符串中的OData $ Skip。例如:

  • 如果查询字符串包含?$ top = 10&amp; $ skip = 10,则不会返回结果。
  • 如果查询字符串包含?&amp; top = 12&amp; skip = 10,则会返回(2)结果。

我要做的是阻止框架将Skip应用于我的结果集,因为查询提供程序已经实现了跳过。是否有ODataQuerySettings可以设置为阻止跳过这个双重应用程序?

编辑: 经过进一步调查,当我从查询字符串中删除$ count = true时跳过(和顶部)函数按预期方式运行。这让我相信我实现$ count = true的方法是不正确的。从我的调试会话中可以看出,当$ count = true在查询选项中时,queryable将表达式树应用于它两次,一次返回类型为long,然后再次没有包装countlong表达式。我已尝试在第一次传递时返回计数,然后在第二次传递时返回正确的查询,但这会导致跳过表达式的延迟应用。这里似乎有一些非常基本的东西。

4 个答案:

答案 0 :(得分:6)

在阅读Github问题列表时,我发现了这篇文章:OData PageResult method ignoring count parameter when using EnableQuery attribute #159。似乎是问题的结果是EnableQuery Attribute和参数化的Get构造函数组合使用ODataQueryOptions。使用这两个意味着您将实现构造函数查询选项,应用查询表达式,然后框架将应用它可以在应用属性的方向上的过滤器;因此,重复使用像skip,top和orderby这样的东西。

答案 1 :(得分:3)

因为我想自己处理跳过,但我仍然想要EnableQueryAttribute的其他功能,我看了一下,发现它是虚拟的。我创建了一个派生类,然后尝试覆盖ApplyQuery方法。不幸的是,ODataQueryOptions只在其属性上有私有集,所以我在一点反思中忍不住。 (感觉很脏,但是嘿..)

public override IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions)
{
    var skipOption = new SkipQueryOption("0", queryOptions.Context);
    typeof(ODataQueryOptions).GetProperty("Skip").SetValue(queryOptions, skipOption, null);

    return base.ApplyQuery(queryable, queryOptions);
}

当skip选项现在为0时,它不会在构造响应时应用它,也不会再使用“double skip blues”。

答案 2 :(得分:0)

我发现,如果您正在使用swagger / swashbuckle,则一起删除该属性将导致swashbuckle不会生成某些OData选项。创建自己的属性,该属性将覆盖ApplyQuery方法以不执行任何操作,仅返回原始查询即可。

    // OData framework's EnableQuery attribute will apply query's again, after we have already applied the query to the result set
    // (So For e.g. you will get Top and Skip applied again on your results that have already had top and skip applied
    // this is a workaround the disables client side queries until this is fixed.
    // https://github.com/OData/WebApi/issues/159
    public class EnableCustomQueryAttribute : EnableQueryAttribute
    {
        public override IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions)
        {
            return queryable;
        }
    }

答案 3 :(得分:0)

根据slamb2k的响应,提供了更多最新版本:

public override IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions)
{
    var parser = typeof(ODataQueryOptions).GetField("_queryOptionParser", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(queryOptions) as ODataQueryOptionParser;
    typeof(ODataQueryOptions).GetProperty("Skip").SetValue(queryOptions, new SkipQueryOption("0", queryOptions.Context, parser), null);
    typeof(ODataQueryOptions).GetProperty("Top").SetValue(queryOptions, new TopQueryOption("0", queryOptions.Context, parser), null);
    typeof(ODataQueryOptions).GetProperty("OrderBy").SetValue(queryOptions, new OrderByQueryOption("0", queryOptions.Context, parser), null);

    return base.ApplyQuery(queryable, queryOptions);
}