我有一个DbSet<Items>
集合。
主键是 Guid。 我不想按此主键排序。 我想按名为"Order"的可编辑十进制属性排序。
我拥有的代码非常简单,在用户将"$top"参数放入请求之前,它运行良好:
public class ItemsController : ApiController
{
protected DbContext ctx = // ...
// GET api/documents
[EnableQuery()]
public IQueryable<Item> Get()
{
return ctx.Items.OrderBy(o => o.Order).AsQueryable();
}
当用户将"$top"放入查询字符串时,顺序会全部搞砸(它可能会强制由主键完成排序,以获得一致的分页结果 - 但是,在我的情况下,这会产生相反的效果,它阻止我获得一致的分页结果)。
我尝试将.AsQueryable()
移动到查询的早期(在 .OrderBy(...)
子句之前),我尝试过没有.AsQueryable()
,我尝试过使用两个 AsQueryable 等。
此表中将有很多项目,因此需要通过IQueryable
来完成(通过IEnumerable
枚举 Web 服务器上的所有项目在这里不是一个选项)。
到目前为止,唯一有效的方法是从客户端传入"$orderby=Order",但我不想强迫这样做(似乎很容易被遗忘)。
1.) 我能做些什么来使我的 Order
属性排序成为这里的默认行为?
2.)或者做不到这一点,是否有任何方法可以欺骗WebApi/OData,使其认为指定了自定义的"$orderby=Order"子句?
要覆盖默认排序顺序,您需要将属性 EnsureStableOrder of EnableQueryAttribute 设置为 false,如下所述:
true 值指示应在以下情况下修改原始查询 是保证稳定排序顺序所必需的。假值表示 在不修改查询的情况下,可以认为排序顺序是稳定的。 确保排序顺序稳定的查询提供程序应设置此值 到假。默认值为 true。
因此,在您的代码中,更改操作属性,如下所示:
// GET api/documents
[EnableQuery(EnsureStableOrdering = false)]
public IQueryable<Item> Get()
{
return ctx.Items.OrderBy(o => o.Order).AsQueryable();
}
您可以在控制器中手动调用 odata。这应该创建正确排序的 IQueryable,然后应用 $top 和任何其他 odata,如 $filter 和 $skip。现在,您不必返回导致问题的 IQueryable,因为实际查询稍后在管道中执行。
public class ItemsController : ApiController
{
protected DbContext ctx = // ...
public IEnumerable<Item> Get(ODataQueryOptions<Item> odata)
{
var collection = ctx.Items.OrderBy(o => o.Order);
if (odata == null)
{
//return a default max size of 100
return collection.Take(100).ToList();
}
var results = odata.ApplyTo(collection.AsQueryable()) as List<Item>;
//still provide a max incase the $top wasn't specified.
//you could check the odata to see if $top is there or not.
return results.Take(100);
}
}
有关详细信息,请参阅 WebApi 文档。