OData(01) - 使用OData高效构建后台服务
使用OData高效构建后台服务
如题本文是要说OData的,无论了解还是不了解都可以看下,本文的前半段无论是做NET还是JAVA或者其他的朋友都同样适用,不过还是以NET为样例说明,后半段就有点晦涩看大家心情了。
开发后台的问题
这里要先称述下后台服务的发展历程,从WebService的出现后到现在出现了很多后台服务相关的概念及框架概念有服务化、Restful、微服务等等,NET中的框架例如WCF、WCF数据服务、WCF Rest、MVC最后到WebAPI。
到目前为止,无论出现了多少概念还是框架,大家都还是做着查询及更新这两件事(对于基于数据库的系统而言)例如:
- 请求数据 -> 后台服务 -> 查询数据库 -> 返回结果
- 提交数据 -> 后台服务 -> 提交数据库 -> 返回结果
各种各样的服务归根结底都在处理若干这样的请求,然而问题来了,同样的事情必然导致了很多重复性劳动,这里我们拿一个销售系统为例来说明,比如系统中有一组客户数据前端有如下需求:
- 维护客户界面,用户可以浏览并编辑自己创建的客户。
- 编辑客户界面,用户可以看到当前选中的客户所有信息。
- 选择客户界面,用户可以看到系统中所有客户
- 在订单界面,用户可以从当前客户的收货地址中选择一个地址
- 以上所有列表界面都需要分页数据
- 以上所有查询客户都需要可以按名称、代码、地址等等10来个属性来过滤
- 可能你的系统比较大还需要外部系统同步数据
这么多需求对于后台服务而言实际只是为了查询和提交客户这张数据表而已,但是这确实是每一个系统都普遍存在的需求非常之多,当然你会因为系统的需求增长,对于类似像客户这样的数据查询及提交需求会层出不穷。
这里我们NET的WebApi为框架列举下常规的做法:
- 每种查询都定义一个API方法,这样做有点机械不过也是最为简单实用而且不会影响到以前的代码。
- 有点架构思想的方法就是会定义一个公共的API方法传入一个字典表示查询的参数,这样可以在服务端动态拼SQL或Lambda表达式
- 之前我有想过将LINQ序列化,然后再到服务端反序列化用于生成查询,确实是有几种LINQ序列化的类库。
以上是我能想的比较常规的做法,当然可能还会有更好的做法大家可以一起讨论。当然随着项目的越来越大,各种方法或者拼接SQL的相关代码会变的多起来,多到会让人觉的很枯燥,这种代码如果放在一起会有非常高度的相似性。当然解决这个问题就是本文要说的OData。
简化的数据服务OData
上面说了这么多,其实只是为了想表达在普通业务系统中我们会有很多重复性的拼SQL或者是Lambda表达式的工作,如果我们归纳一下会发现,大多数情况服务端只需要约束用户可操作的数据范围即可,对于数据的操作放在前端做更为贴切一点。还是以上面这个例子来说:
- 查询数据,无论我们是用A条件去查还是用B条件去查客户,这都是为了前端呈现所需要的,对于服务端其实我们只要限制用户查询客户的最大范围就可以了,如果能在前端构建查询条件然后服务端自动执行这样开发就很高效
- 提交数据,也是同样的道理只要在一个地方限制用户可提交的范围,客户端自行组装提交的内容即可。
如果能按上面两条去构建服务端,这样会大大简化服务端。纵观各种各样的概念还有框架,最后我在Odata身上找到了结果,就像官网说的 OData - the Best Way to REST(最好的REST服务实现方式),目前最新协议版本4.0.1。
OData可以直接使用URL构建过滤、分页、分组、展开相关数据等数据操作,例如下面这个官方例子:
- all products with the Name 'Milk' that also have a Price less than 2.55:
http://host/service/Products?$filter=Name eq 'Milk' and Price lt 2.55
- all categories and for each category the references of all related premier products with a current promotion equal to null
http://host/service/Categories?
$expand=Products/Sales.PremierProduct/$ref($filter=CurrentPromotion eq null)
有兴趣的朋友可以去官网看下URL协议全文。
OData Net 实现
上面说的只是OData协议,不过各个语言都有了对OData的实现,这里我们只讨论NET,在NET的实现有若干种,对于服务端实现其实只有一种就是ASP.NET Web API OData,这也是目前实现最好的,如果你再结合Entity Framework真正做到上文所说的客户端通过URL可以直接构建查询,服务端不用做任何多余代码。开发起来非常高效、快捷,当然代码越少出Bug的几率也越少。
万恶的 EDM
看标题就知道我是想吐槽它了,本来 EDM 是从Entity Framework发展出来的,这个设计的初衷是非常好的,大家想下做为一个ORM必然需要一个数据模型来记录与数据库对象结构的映射关系,然而 EDM 很好的在这里发挥了作用。做为一个良性的开发需要明确模型,项目越大明确数据模型就越重要。不过凡事都不能决对化,然而EF与WebApi OData都是依赖EDM,它们都不会接受EDM中不存在的数据结构、方法,而且EDM模型一旦被构建就不能再修改。这里举个例子来说明下:
上面说所有OData协议再强大也还是有些应用场景需要自定义方法来解决,在WebApi OData中是可以构建自定义方法,如果你想传入一个复杂的变量,就必须要在EDM中注册这个类型,返回结果也是一样,这样也就表明你不可能返回多种类型的数据,也不可能返回匿名数据,还有提交数据你也不可能用DTO对象来提交了,本来WebAPI是一个很灵活的框架,但是一旦应用到WebApi OData中就变的不灵活了。
Web API OData 的缺点
使用OData也有很长时间了,发现了下面几个缺点分享给大家:
- 上面提到的EDM问题。
- 在这个框架中你一定要用EF或者EFCore(理论上,目前我还没实验过,因为目前在NET Core下的版本才刚刚算稳定)才能实现URL自动转SQL的工作,否则还是要自己手写,而且解析这个结构也是非常难度的工作。
- 我们可以在服务端过滤用户不能查询的数据行,不过数据列就没办法过滤。
- 有时不想某些客户端排序或过滤数据也是不可行的,在Web API OData中这些定义必须写死在代码中。
- 对于数据提交而言Web API OData中一次只能提交一条数据,如果有上万条就杯具了
- 在Web API OData中是可以批量提交请求的,不过你不能批量查询数据(我很好奇为什么)。
- 在Web API OData中批量提交最多只能提交大约2000多个子请求(每个子请求也只能提交一条数据),这里的事务提交是要自己实现的,框架中没有提供,实现这个功能还是相当复杂的(新手不适合)。
后记
原来看过一遍文章写开源是为了避免重复造轮子,现在看来这样看情况了,不是每个开源的框架都是那么完美,其实不完美也不重要,重要是能快速发展。相信有不少人都有这样的体会,之前的EntityFramwork6就是这样,更新太慢后面直接不更新了,所以才自己开发了一个更实用的ORM Mego 。
现在Web API OData也是如此,估计短期内也解决不了上面这些问题,所以我又再一次造轮子,首先需要构造OData协议的文法,我会在下一遍博客中给大家分享。
以上是我的个人见解,仅供参考。