WebAPI OData $Skip 自定义 IQueryable 双应用
WebAPI OData $Skip on custom IQueryable double applied
我已经实现了通过 WebAPI OData 端点公开的自定义 IQueryable。 controller的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);
}
我看到的奇怪行为是,在 returned PageResult 之后应用了查询字符串中的 OData $Skip。例如:
- 如果查询字符串包含 ?$top=10&$skip=10,则不会有结果 return。
- 如果查询字符串包含 ?&top=12&skip=10 将会有 (2) 个结果 returned.
我想要做的是阻止框架将 Skip 应用到我的结果集,因为查询提供程序已经实现了 skip。是否可以设置 ODataQuerySettings 以防止这种重复应用跳过?
编辑:
经过进一步调查,当我按预期从查询字符串跳过(和顶部)函数中删除 $count=true 时。这让我相信我实现 $count=true 的方法是不正确的。从我的调试会话来看,当 $count=true 在查询选项中时,可查询对象将表达式树应用于它两次,一次是 return 类型的 long,然后又一次没有包装 countlong 表达式。我已经尝试 return 在第一次通过时计算计数,然后在第二次通过时进行适当的查询,但这会导致跳过表达式的延迟应用。我在这里似乎缺少一些非常基本的东西。
在阅读 Github 问题列表时,我遇到了这个 post:OData PageResult method ignoring count parameter when using EnableQuery attribute #159。问题似乎是 EnableQuery Attribute 和采用 ODataQueryOptions 的参数化 Get 构造函数的组合。使用两者意味着您将实现构造函数查询选项,应用查询表达式,然后框架将应用它可以根据应用属性的方向进行的过滤器;因此双重应用诸如 skip、top 和 orderby 之类的东西。
因为我想自己处理跳过,但我仍然想要 EnableQueryAttribute 的其他功能,所以我看了一下,发现它是虚拟的。我创建了一个派生的 class,然后试图重写 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);
}
随着跳过选项现在为 0,它在构建响应时不再应用它,不再应用 "double skip blues"。
我发现,如果您正在使用 swagger/swashbuckle,将属性一起删除将导致某些 OData 选项无法由 swashbuckle 生成。创建您自己的属性来覆盖 ApplyQuery 方法以不执行任何操作,只是 return 原始查询按预期工作。
// 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;
}
}
根据 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);
}
我已经实现了通过 WebAPI OData 端点公开的自定义 IQueryable。 controller的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);
}
我看到的奇怪行为是,在 returned PageResult 之后应用了查询字符串中的 OData $Skip。例如:
- 如果查询字符串包含 ?$top=10&$skip=10,则不会有结果 return。
- 如果查询字符串包含 ?&top=12&skip=10 将会有 (2) 个结果 returned.
我想要做的是阻止框架将 Skip 应用到我的结果集,因为查询提供程序已经实现了 skip。是否可以设置 ODataQuerySettings 以防止这种重复应用跳过?
编辑: 经过进一步调查,当我按预期从查询字符串跳过(和顶部)函数中删除 $count=true 时。这让我相信我实现 $count=true 的方法是不正确的。从我的调试会话来看,当 $count=true 在查询选项中时,可查询对象将表达式树应用于它两次,一次是 return 类型的 long,然后又一次没有包装 countlong 表达式。我已经尝试 return 在第一次通过时计算计数,然后在第二次通过时进行适当的查询,但这会导致跳过表达式的延迟应用。我在这里似乎缺少一些非常基本的东西。
在阅读 Github 问题列表时,我遇到了这个 post:OData PageResult method ignoring count parameter when using EnableQuery attribute #159。问题似乎是 EnableQuery Attribute 和采用 ODataQueryOptions 的参数化 Get 构造函数的组合。使用两者意味着您将实现构造函数查询选项,应用查询表达式,然后框架将应用它可以根据应用属性的方向进行的过滤器;因此双重应用诸如 skip、top 和 orderby 之类的东西。
因为我想自己处理跳过,但我仍然想要 EnableQueryAttribute 的其他功能,所以我看了一下,发现它是虚拟的。我创建了一个派生的 class,然后试图重写 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);
}
随着跳过选项现在为 0,它在构建响应时不再应用它,不再应用 "double skip blues"。
我发现,如果您正在使用 swagger/swashbuckle,将属性一起删除将导致某些 OData 选项无法由 swashbuckle 生成。创建您自己的属性来覆盖 ApplyQuery 方法以不执行任何操作,只是 return 原始查询按预期工作。
// 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;
}
}
根据 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);
}