武装你的WEBAPI-OData便捷查询

目录(可能会有后续修改)

武装你的WEBAPI-OData入门

武装你的WEBAPI-OData便捷查询

武装你的WEBAPI-OData分页查询

武装你的WEBAPI-OData资源更新

武装你的WEBAPI-OData之EDM

武装你的WEBAPI-OData格式转换

武装你的WEBAPI-OData使用Endpoint

Introduction

前文大概介绍了下OData,本文介绍下它强大便捷的查询。(后面的介绍都基于最新的OData V4)

假设现在有这么两个实体类,并按照前文建立了OData配置。

public class DeviceInfo { [Key] [MaxLength(200)] public string DeviceId { get; set; } //....// public float Longitude { get; set; } public Config[] Layout { get; set; } } public class AlarmInfo { [Key] [MaxLength(200)] public string Id { get; set; } public string DeviceId { get; set; } public string Type { get; set; } [ForeignKey("DeviceId")] public virtual DeviceInfo DeviceInfo { get; set; } public bool Handled { get; set; } public long Timestamp { get; set; } }

Controller设置如下,很简单,核心就几行:

[ODataRoute] [EnableQuery] [ProducesResponseType(typeof(ODataValue<IEnumerable<DeviceInfo>>), Status200OK)] public IActionResult Get() { return Ok(_context.DeviceInfoes.AsQueryable()); } [ODataRoute("({key})")] [EnableQuery] [ProducesResponseType(typeof(ODataValue<IDeviceInfo>), Status200OK)] public IActionResult GetSingle([FromODataUri] string key) { var result = _context.DeviceInfoes.Find(key); if (result == null) return NotFound(new ODataError() { ErrorCode = "404", Message = "Cannnot find key." }); return Ok(result); } 集合与单对象

使用OData,可以很简单的访问集合与单个对象。

//访问集合 GET :9000/api/DeviceInfoes //访问单个对象,注意string类型需要用单引号。 GET :9000/api/DeviceInfoes('device123') $Filter

请求集合很多,我们需要使用到查询来筛选数据,注意,这个查询是在服务器端执行的。

//查询特定设备在一个时间的数据,注意这里需要手动处理一下这个ID为deviceid。 GET :9000/api/AlarmInfoes('device123')?$filter=(TimeStamp ge 12421552) and (TimeStamp le 31562346785561)

$Filter支持很多种语法,注意范围

$Expand

在查询alarminfo的时候,我很需要API能够返回所有信息,其中包括对应的deviceinfo信息。但是标准的返回是这样的:

{ "@odata.context": "http://localhost:9000/api/$metadata#AlarmInfoes", "value": [ { "id": "235314", "deviceId": "123", "type": "低温", "handled": true, "timestamp": 1589235890047 }, { "id": "6d2d3af3-2cf7-447e-822f-aab4634b3a13", "deviceId": "5s3", "type": null, "handled": true, "timestamp": 0 }] }

只有一个干巴巴的deviceid,要实现展示所有信息的功能,就只能再从deviceinfo的API请求一次,获取对应ID的devceinfo信息。

这就是所谓的N+1问题:请求具有子集的列表时,需要请求1次集合+请求N个集合内数据的具体内容。会造成查询效率的降低。

不爽是不是?看下OData怎么做。请求如下:

GET :9000/api/alarminfoes?$expand=deviceinfo

通过指定expand参数,就可以在返回中追加导航属性(navigation property)的信息。

本例中是使用的外键实现的。

{ "@odata.context": "http://localhost:9000/api/$metadata#AlarmInfoes(deviceInfo())", "value": [ { "id": "235314", "deviceId": "123", "type": "低温", "handled": true, "timestamp": 1589235890047, "deviceInfo": { "deviceId": "123", "name": null, "deviceType": null, "location": "plex", "description": "99", "longitude": 99, "layout": [] } } 层级查询

实现了展开之后,那能否查询在多个层级内的数据呢?也可以,考虑查询所有longitude大于90的alarmtype。

GET :9000/api/alarminfoes?$expand=deviceinfo&$filter=deviceinfo/longitude gt 90

注意,这里表示层级不是使用.而是使用的/。如果使用.容易被浏览器误识别,需要特别配置一下。

结果如下:

{ "@odata.context": "http://localhost:9000/api/$metadata#AlarmInfoes(deviceInfo())", "value": [ { "id": "235314", "deviceId": "123", "type": "低温", "handled": true, "timestamp": 1589235890047, "deviceInfo": { "deviceId": "123", "name": null, "deviceType": null, "location": "plex", "description": "99", "longitude": 99, "layout": [] } } any/all

可以使用any/all来执行对集合的判断。
这个参考:

GET ?$filter=Emails/any(e: endswith(e, 'contoso.com'))

对于我的例子,我使用

GET :9000/api/DeviceInfoes?$filter=layout/any(e: e/description eq '0')

服务器会弹出500服务器错误,提示:

System.InvalidOperationException: The LINQ expression 'DbSet<DeviceInfo> .Where(d => d.Layout .Any(e => e.Description.Contains(__TypedProperty_0)))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpppzz.html