【问题标题】:OData v4 order by multiple cardinality propertyOData v4 按多个基数属性排序
【发布时间】:2019-12-17 09:07:00
【问题描述】:

我有以下网址:

{{hostUrl}}/odata/TasksOData?$select=Id,Name,State,TaskRuns,LastChangedAt,LastChangedBy&$expand=TaskRuns($orderby=RunAt desc;$top=1;$select=Status,RunAt,RunBy)&$top=16&$orderby=TaskRuns/RunBy

我想从 TaskRuns(RunBy) 中按属性订购任务。 TaskRuns 是一个我只想考虑第一项的集合。

我收到错误:"message": "The parent value for a property access of a property 'RunBy' is not a single value. Property access can only be applied to a single value.",

RunBy 是一个 GUID 字段。 RunAt 出现同样的问题,它是 DateTime 字段。 我用其他场景进行了测试,似乎问题是因为 TaskRuns 是一个集合。即使花费的时间比预期的要长,以下网址也可以工作:

{{hostUrl}}/odata/TasksOData?$select=Id,Name,State,TaskRuns,LastChangedAt,LastChangedBy&$expand=TaskRuns($orderby=RunAt desc;$top=1;$select=Status),Script($select=Name)&$top=16&$orderby=Script/Name desc

后端:OData v4、asp 核心、v7.1.0

如何实现呢?谢谢!

【问题讨论】:

    标签: asp.net-core odata odata-v4


    【解决方案1】:

    oData v4 规范不支持这种类型的嵌套排序。但这并不意味着您无法使用 oData 获得类似的结果集。

    问题是我们想要排序子集合内一行中a属性的顶级响应的结果。

    这不受支持,您只能根据该集合中行的属性对每个集合进行排序。 $orderby 只能引用单例属性路径来对结果进行排序。您可以在 $expand 结果中指定记录的排序顺序,但 OOTB 不能使用集合中任意行的属性来指定顶级排序顺序。

    您检索此类数据的选项分为 3 类:

    1. 在控制器上创建自定义端点以执行结果集

      • 这应该是最容易实现的,但只有在您控制 API 时才可行。
      • 对上述内容的 Linq 查询非常简单,示例控制器函数可能如下所示:

      [HttpGet]
      public IHttpActionResult GetRecentTaskRuns()
      {
          var query = from task in _db.Tasks
                      let lastRun = task.TaskRuns.OrderByDescending(t => t.RunAt).FirstOrDefault()
                      select new
                      {
                          task.Id,
                          task.Name,
                          task.State,
                          // Consider not returning this as a collection at all
                          //TaskRuns = task.TaskRuns.OrderByDescending(t => t.RunAt).Take(1).ToArray(),
                          LastChangedAt,
                          LastChangedBy,
                          // Just return LastRun, instead of TaskRuns.
                          LastRun = lastRun
                      };
      
          return Json(query.OrderBy(t => t.LastRun.RunBy));
      }
      

      不要害怕在你的控制器上创建自定义端点来实现特定的业务需求,你的 API 的目的是方便数据访问,是的 OData 为调用者公开了美妙的语法来完全控制要下载的数据,但是你可以帮助他们。

    2. 将您的查询改为从 TaskRuns 控制器中检索
      • API 端不需要进行任何更改
      • 这会改变结果集和数据的结构
      • 您可以使用$apply 返回一个group by 结果,这将类似于原始要求
      • 有关$apply 的更多详细信息,请参见下文
    3. 覆盖 EnableQueryAttribute 或以其他方式实现您自己的 $orderby 解释器
      • 这远远超出了本次讨论的范围,但是,您可以为$orderby 实现自己的解释器以及如何将其应用于底层数据库查询,如果您有能力这样做的话,您应该考虑选项 1...

    如果您不想或无法修改 API 本身,只有上述选项 2 会为您提供帮助。


    让我们探究一下原因,例如,如果每个Task 有多个TaskRuns,那么应该使用其中的哪一个TaskRuns 来获取RunBy 的值?

    您可能想尝试使用 MIN 或 MAX 函数,但它们不起作用,OData v4 规范中没有以下规定:

    {{hostUrl}}/odata/TasksOData?$orderby=Max(TaskRuns/RunBy)
    or
    {{hostUrl}}/odata/TasksOData?$orderby=TaskRuns/Max(RunBy)
    

    使用 $Apply

    ​​>

    当您想根据子集合限制结果时,我们可以使用$apply 函数,但是类似的规则类型适用于只能对根集合进行操作。因此,切换您的查询,以便子集合 IS 成为根!

    即使使用$apply,我也无法获得与原始查询所暗示的完全相同的结构或顺序,但此查询显示了另一种选择

    注意:如果您的日期字段不是 DateTimeOffset

    ,您将无法使聚合工作
    {{hostUrl}}/odata/TaskRunsOData?  
    $apply=groupby((Task/Id,Task/Name,Task/State,Task/LastChangedAt,Task/LastChangedBy), aggregate(RunAt with max as RunAt,RunBy with max as RunBy)&$top=16&$orderby=RunBy DESC&$count=true 
    
    

    应该返回 16 个项目(或更少),其结构类似于:

    {
        "@odata.context": …,
        "@odata.count": 16,
        "value": [
            {
                "@odata.id": null,
                "RunAt": …,
                "RunBy": …,
                "Task": {
                    "@odata.id": null,
                    "Id": 7,
                    "Name": 'TaskName',
                    "State": 'Pending',
                    "LastChangedAt": …,
                    "LastChangedBy": …,
                }
            },
            … 
        ]
    }
    

    【讨论】:

    • 感谢您的回复,我会更清楚,我会更新问题。 RunBy 是一个单一的值,一个 GUID 字段。我认为问题在于 TaskRuns 是一个集合。
    • 那么TaskRuns 中的哪一个应该确定要使用的RunBy 属性?或者这是一个单例集合? (通常只有一条记录?)
    • 您好,您在这个问题上有什么进展吗?我遇到了同样的问题。似乎是一个很大的限制!你知道规范是否支持这个吗?干杯。
    • 我已经重新格式化了我的回复,希望这更容易解释。 @user644698 如果您使用特定查询发布您自己的问题,我们可以在那里为您提供帮助(在此处留下评论以便我们关注)
    • 感谢您的快速回复。我在这里发布了一个新问题...stackoverflow.com/questions/59370734/…。我想你已经回答了我上面的问题,即它不能完成 ootb。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-12
    • 2011-11-24
    • 1970-01-01
    相关资源
    最近更新 更多