源宝导读:明源云ERP的底层架构正在向.Net Core跨平台迁移,我们在过程中遇到了部分不兼容的问题。本文将介绍技术团队如何解决.Net Core与已有MVC框架不兼容问题的解决方案。
一、背景
云ERP的建模平台是基于.NET Framework构建的,在向.NET Core迁移的过程中会遇到各种问题,向下兼容是一定要考虑的,框架本身的特性兼容交给微软,但是还有很多的特性可能是旧版建模平台需要支持的特性,比如多参数绑定、服务工厂Controller激活。今天就聊聊在向.NET Core迁移时如何兼容以上两个特性。
二、概念解释
服务工厂
云ERP的服务工厂是建模平台的内部服务管理实现,用于创建ERP各个领域服务类实例,在创建服务对象的过程中会进行加工,根据二开模式(Before/After/Override)动态创建携带插件方法的代理类型,以达到易二开的技术特性。
应用服务
应用服务是云ERP的入口,它们是由一系列后缀名为AppService的Class组成的,概念上相当于MVC的Controller,作用也一样。
自定义路由
云ERP的路由中添加了命名空间地址,官方的模板格式无法满足,本文利用ControllerModelConvention在Apply时为每一个Action批量配置路由。路由格式: `api/ {controller.ControllerType.FullName} /{action.ActionName}.aspx`
Controller激活
云ERP也是基于MVC构建的,Controller也是服务,也是需要可扩展,自然需要通过服务工厂去创建,这就需要找到.NET Core MVC中Controller激活的时机,使用服务工厂创建替换掉默认的类实例化过程。
多参数绑定
.NET Core MVC 中天生是不支持将[FromBody]数据源绑定到Action的多个参数上的。这一特性在微软官方文档中有解释(参考:微软对FromBody的参数绑定限制说明)。由于这个限制,我们必须在向.NET Core MVC迁移时尝试解决这个问题,思路与Controller激活的差不多,就是找到参数绑定的默认实现,替换掉它。
为了大家能统一语言,请提前了解下.NET Core MVC的基本特性,这里就不再阐述,需要了解的读者请点击传送门。
三、面临的问题
让MVC路由到云ERP的AppService
.NET Framework MVC 升级到 .NET Core MVC 在实现原理上有很大不同,由服务管线变成了中间件管道,无法直接使用HttpHandler实现了,必须重新实现Controller路由到激活的过程。
那么将面临两个问题:
自定义路由,首先要能让MVC认知AppService并能按照云ERP的模板规范进行路由。
路由到AppService后激活的方式得用服务工厂 ServiceFactory来创建,这样才能兼容二开的插件模式。(注:服务工厂中创建的是AppService的代理类的实例,携带二开定义的插件方法)
支撑多参数绑定
云ERP多参数绑定的解决方案是利用的ClownFish.net框架中的 ClownFish. Web.Serializer. JsonnetDataProvider 来解决的(ClownFish.net明源云的架构师FishLi多年前的作品,已在Github上开源)。
迁移到.NET Core MVC中就需要自己实现了,原理差不多,这里不多讲有兴趣的可以看源码。
四、解决思路
要在.NETCore MVC中兼容云ERP的MVC特性,核心要解决的就是以下三点:
自定义路由。
controller激活。
多参数绑定。
我们一个一个来解决。
自定义路由
首先我们得让MVC认识AppService,不然无法做后面的事情。创建一个 AppServiceFeatureProvider 继承 ControllerFeatureProvider 用来将AppService提供给MVC框架作为控制器使用。并且将AppServiceFeatureProvider提供程序加入MVC的特性提供者集合中。最后在 ConfigureServices 中调用 AddClownFish()。
接下来,就是要给每一个AppService的方法(Action)设置自定义路由。这里利用的是.NET Core MVC的一个Convention 特性,不了解的读者先阅读一下微软文档。
(https://docs.microsoft.com/zh-cn/aspnet/core/mvc/controllers/application-model?view=aspnetcore-3.0#conventions)。
创建一个 AppServiceModelConvention 类型 实现 IControllerModelConvention的Apply 接口。为ControllerModel的每一个Action创建属性路由(AttributeRouteModel),代码如下,为了保证理解容易,我隐藏了部分不重要的代码,文末有Demo源代码链接可以下载。在AddClownFish扩展方法中把AppServiceModelConvention加如MVC的约定列表。
Controller激活
所以分解任务就是:
创建 ClownFishControllerActivator 替换默认的Controller激活器实现
实现ERP自定义的激活器,用服务工厂创建AppService实例
把ClownFishControllerActivator加入服务列表中
代码比较简单:
这里值得一提的是,微软在MVC框架中确实实现了一个默认的激活器实现,但是如果你不去主动使用它,默认他是不使用的,会跳过。避免被坑,我截图说明了一下:
多参数绑定
多参数绑定实现起来比较费劲,主要是因为参数绑定的过程是封闭的,这里利用了篡改缓存的实现,用自定义的Lambad表达式构建了一个新的实例来替换掉默认的实现。
思路:
实现 IActionInvokerProvider 替换掉默认实现。
在Action执行时构建一个自定义的Bind函数替换掉缓存中的Bind函数,返回一个新的缓存对象。
自定义的Bind函数中就可以拿到HttpContext了,这样就可以为所欲为了,自己创建一个JsonTextReader来自由读取Body然后依次绑定到Controller的arguments列表中。
然后把新的缓存对象塞回缓存字典中。
代码如下:
四、总结
阅读官方文档发现 .NET Core MVC提供了丰富的扩展机制,这为上层框架的发展提供了可能,通过阅读MVC源码可以学习微软程序员的代码设计思想,思考并提炼应用到ERP的产品代码设计中,这也是开源的一种福利吧。
最后附上Demo源码地址:
https://github.com/zongzijie/study/tree/master/CSharp/SampleForAspNetCoreMvc
------ END ------
作者简介
伍同学: 研发工程师,目前负责售楼系统的相关设计开发工作。
也许您还想看
ERP缓存实践经验分享
.Net最小工作线程对应用程序性能的影响
记一次生产环境CPU100%排查实践
如何使用有序GUID提升数据库读写性能