Asp.net Mvc模块化开发之分区扩展框架
对于一个企业级项目开发,模块化是非常重要的。
默认Mvc框架的AreaRegistration对模块化开发真的支持很好吗?真的有很多复杂系统在使用默认的分区开发的吗?我相信大部分asp.net的技术团队最开始都研究过分区,甚至在实际项目里面有尝试运用,但是碰到了种种问题"各种坑",最后回头是岸放弃了(我们的团队也碰到了类似问题,也有人评论中说起,直接搜索asp.net mvc分区也有不少类似信息)。
有人说asp.net Mvc框架就不适合做模块化开发,我们可以弄一个其他框架来做企业级的分模块开发,确实现在好像已经有类似的开源项目。
但是,这个确实没有必要,这里对Mvc框架做了一个简单的扩展,基本能做到分模块开发和单个简单Mvc项目开发没有太大区别,并分享给大家。
本文中的栗子是使用.net4.0、Mvc4.0及Unity2.0(企业库4.0)的。
本分区扩展集成了IoC和分区DI(依赖注入)及分区过滤器的支持。
感兴趣的同学请继续,用AreaRegistration有不爽的看官请拭目以待...
1、 Asp.net MVc模块化开发之分区扩展框架(送源码)
2、 Asp.net Mvc模块化开发之“开启模块开发、调试的简单愉快之旅”
3、 Asp.net Mvc模块化开发之“逻辑(项目)复用”
3.1、 不同角色或者权限的逻辑(项目)复用(分区过滤器的应用)
3.2、 不同业务的逻辑(项目)复用(DI(依赖注入)的应用)
4、 Asp.net Mvc模块化开发之“项目(分区)拆分”
5、 Asp.net Mvc模块化开发之“部分版本部分模块更新(上线)”
一、先说一下我用Mvc不爽的地方,看大家是否有同感
1、分区类(继承AreaRegistration)是个"特殊"类型,只能创建一个对象,而且只能简单的"New",不能使用构造函数、属性和方法来初始化对象
好在Mvc是开源的,我们从AreaRegistration的源码中发现是,Mvc是通过AreaRegistration.RegisterAllAreas()初始化所有分区
从上图可以看到Mvc先通过把每个继承AreaRegistration的类型找出来,再通过Activator.CreateInstance创建对象。在我看来类型和对象应该是一对多的关系,分区类也一样。我希望对分区的字段和属性进行不同的初始化以便可以使用同一个类型创建不同的分区。
2、分区的完整路径提前写死了,哪怕是换一下前面的路径都休想
比如有个评论的功能,我希望做成一个分区。新闻的评论地址规则为/News/Comment/,博客的评论地址规则为/Blog/Comment/,使用Mvc默认分区就只能定义为/Comment/了
3、路由初始化需要使用Global.asax
其一、但是多个分区部署同一站点用谁的Global.asax呢?有人说每个Global都定义RegisterAllAreas,用谁的都行。但是对于程序员这个大多都是偏执狂的人群是很难接受的。如果哪个项目下线,正好整个站点都使用的是这个项目的Global,那就都挂了!有风险啊。
其二、那就建一个项目为主项目,其他项目都以分区的形式定义?What?谁是主项目?偏执狂纠结中...
其三、建一个项目为空项目为主项目,只有Global和RegisterAllAreas,还是觉得挺别扭的。
其四、定义一个HttpModule在Init中执行RegisterAllAreas。这个还算靠谱,建议大家使用。本扩展框架也是定义一个HttpModule来初始化一些Mvc的配置达到扩展目的的。
4、Mvc很多种过滤器,但默认配置过滤器的方法只有两种,一种是Attribute,一种是GlobalFilters(全局过滤器)
其一、Attribute定义过滤器太细了,要每个Controller或者Action去加,不能漏哦,特别是权限判断
其二、GlobalFilters拦截所有请求,如果使用分区开发的项目,每个分区有自己的过滤器需求,该需求可能与其他分区冲突(影响其他分区的Controller正常执行)。
其三、分区我希望复用,其中的Controller当然也要服用,在不同的部署中有使用不同过滤器或者过滤器参数的需求,Attribute定义的过滤器很难满足要求。
总之我希望有一种过滤器只影响当前部署的分区,不需要提前和分区及Controller绑定(高耦合),部署(运行时)的时候再和指定分区关联(当然要借助IoC容器,这里使用的是微软的Unity框架)。
5、Mvc4.0支持DI,如果没有IoC容器的支持,很鸡肋
其一、Mvc4.0默认的DI基本上没有什么作用,必须扩展。
其二、每个分区有不同的DI(依赖注入)需求,我希望每个分区可以配置成不同的。
6、使用默认的分区开发出现重名的控制器错误或者视图渲染出错
6.1 在主项目中增加分区,如果新加分区的控制器(Controller)类名与主项目中控制器同名,导致主项目的控制器出错,但是分区的控制器正常
这是个挺要命的问题,新加分区不出错,主项目的控制器出错,这个问题很难定位,我们一般是哪里出错找哪里。但是我们在主项目的控制器里找不到错误,而且这个控制器原来是正常工作的。新加了分区功能,但他"干掉"原来正常的功能!!!(很恐怖不是吗?如果知道 原因当然好处理,关键是很多人不会去怀疑新加并正常运行的分区,新分区的开发人员也可能会"理直气壮"的否认是自己的问题。)
6.2 新加分区的视图出错
一种原因是默认分区新加视图的位置和普通项目不一样,搞不好弄错位置
还有一种原因,主项目的视图与新分区的视图冲突,这个就有点恶心了
总之默认的分区开发总是和普通的项目(Mvc)开发有很多的不同,导致分模块开发的困难和故障,甚至使人敬而远之,彻底不分区。
二、Mvc分区扩展方法
1、我们继续从源码查找分区初始化的过程
RegisterAllAreas调用CreateContextAndRegister
但是CreateContextAndRegister是一个internal保护的方法,不让我们用啊!好在这个方法没有几行代码,我们直接再写一个类似方法就可以了。
2、Mvc最重要的就是路由规则,这个也要搞清楚
这个简单,随便建一个分区就可以看得很清楚
原来Mvc是通过RegisterArea调用AreaRegistrationContext的MapRoute方法来设置路由规则的;而AreaRegistrationContext对象创建就更简单,直接New出来的(参考前面CreateContextAndRegister的源码截图)!
现在基本上都搞清楚了,可以动手进行扩展了。
3、首先对分区扩展类(Area),我们来增加一些功能
我这里定义了一个类型Fang.Mvc.Area继承AreaRegistration。这个是可以按个人爱好加一些功能的,最好是一些常用功能,每个分区项目的个性化功能可以通过再次继承这个类型来扩展
3.1 扩展分区基本属性
通过Name属性实现分区名的配置(注意:同一个站点下分区名不能重复)。
通过Path属性分区路径前缀,就是分区的路由规则前都加一个这个路径。其一、大家知道,如果路由规则冲突化,Mvc无法找到正常的路由规则,这个很要命,增加一个部署是配置的路径就很有必要了。其二、我的需求比较奇特,我希望同一个站点下可以部署多个相同的分区,就像前面说的我们要给/News/和/Blog/都部署Comment分区,访问路径不一样,访问的数据库或者数据表也可以不一样,对已数据源不一样就靠DI了,后面再继续说。
通过NameSpaces属性配置当前分区查找Controller的范围。如果没有配置就按Mvc默认处理,按当前分区类的命名空间。
这样我们的每个分区是独立的(通过NameSpaces隔离),每个分区既可以独立运行调试和可以和其他分区部署到一起也不会相互冲突。和普通简单的Mvc项目几乎没有区别,大大减少开发和学习成本。当然有人会说,用这个分区扩展框架不是还要现学容器框架(Unity),这个确是事实。但是容器框架(Unity)的作用不仅仅是管理好几个分区,容器的Ioc、Aop等作用对项目开发那是如虎添翼,容器技术几乎是大型系统开发必备工具,小小的分区框架集成了容器本身也是该框架的一大亮点。
4、定义一个路由规则注册类(AreaRoute)来达到把Area的配置应用到路由注册
AreaRoute通过调用AreaRegistrationContext的对应方法注册
AreaRoute调用Area的CheckName、CheckUrl和CheckNameSpaces来实现Area的配置
把分区的路由规则保存到Area的_routes列表中备用(这里不详述作用,不在本文探讨范围内)
5、现在开始搞定分区过滤器
5.1 在Area类增加属性Filters配置分区过滤器
5.2 定义AreaFilterProvider类型来调用Area的Filters
AreaFilterProvider继承Mvc框架的IFilterProvider接口
在分区初始化时把AreaFilterProvider添加到Mvc的FilterProviders.Providers中,任务完成。
6、该轮到期盼已久DI容器了
6.1 首先定义Fang.Mvc.Container类来封装一下Unity容器
这个使用其他容器工具也是可以的,甚至封装一个工厂,支持多种容器也未不可。
至于有人说Unity有"bug",不适合做DI。我使用Unity容器有几年了,还没完全搞明白Unity,我认为Unity容器还是挺博大精深的,足够我用了。至于"bug"也算不上bug,最多是坑,多踩踩就平了。至于Unity(及企业库)有多挺博大精深,不在本文探讨范围,敢兴趣的可以自己研究,找高手探讨。
找我交流也行(但是我还在学习中,认识可能还肤浅或者有错误,最好不要误导了大家)。
6.2 增加属性DependencyContainer配置DI(依赖注入)容器的名字
6.3 定义AreaDependencyResolver类来实现分区配置和DI配置的结合
AreaDependencyResolver继承Mvc框架的IDependencyResolver接口
接下来就简单了,按Http上线文信息获取分区配置,调用容器工厂,调用容器实现GetService方法即可。
7、万事俱备了,借HttpModule的东风吹起来
定义类AreaMergeModule继承asp.net的IHttpModule,在Init方法中一通调用初始化以上扩展的功能的初始化。
调用容器工厂获取Mvc容器,在容器中获取所有的Area对象并逐个初始化,再初始化AreaDependencyResolver。
三、重头戏开场,炒"栗子"
1、一个站点配置两个分区,分别调用不同的数据源
源码路径:\Test\MvcApplication1
1.1 先看分区及容器配置
配置文件:\Test\MvcApplication1\ConfigFiles\unity.config
以上使用分区类MvcApplication1.RouteConfig定义两个分区(A和B),每个分区的名字和路径等都配置不一样。
另外还额外定义了两个依赖注入的容器。名字分别为两个分区配置的依赖注入的容器名。
以上还可以看出分区类和分区的Controller没有依赖关系,事实上只要路由规则没有特殊路由及其他要求,可以使用定义好的默认分区类Area。而且分区定义是按命名空间,事实上原有复杂系统可以在不修改原项目的情况下按逻辑(命名空间)拆分为多个分区,以后看情况再逐个分区拆分为独立项目,优化项目结构。
如果有同学说你上面的配置看不懂,不好意思要补课了,找度娘,搜索"Unity2.0容器配置"(小心有一个叫Unity3d的东西是做游戏的,和这个没关系)。
1.2 再看HttpModule配置
配置文件:\Test\MvcApplication1\Web.config
system.web的httpModules在经典模式下起作用,system.webServer的modules在集成模式下起作用,如果不确定两个都配上就Ok了。
1.3 分区路由配置(与Mvc默认路由配置及分区配置对比)
配置文件:\Normaltest\MvcApplication4\App_Start\RouteConfig.cs
配置文件:\Test\MvcApplication1\App_Start\RouteConfig.cs
以上三种路由配置中,可以看出新改良的分区扩展居然比Mvc默认分区配置和静态路由配置更相似
首先使用该扩展配置路由没有新的学习成本
其次原非分区项目要改成分区项目建一个分区类(扩展后的),把路由规则直接复制过来,意味迁移成本非常低。
1.4 再看一下Controller怎么用DI
代码文件:\Test\MvcApplication1\Controllers\HomeController.cs
HomeController执行依赖一个数据源(Config),但是HomeController并没有定义和调用获取数据源的方法,只是把数据源(Config属性)声明为Dependency的。
1.5 最后看测试效果
例子有点简单,完全说明问题,两个不同路径(分区路径)的Url都访问到HomeController而且数据源(Config属性)自动初始化成功,而且这两个地址调用了不同的数据源。
2、分区过滤器的栗子
源码路径:\Test\Site
2.1 先看分区及容器配置
配置文件:\Test\Site\ConfigFiles\unity.config
这次配置更复杂了一点,定了三个分区,使用两个分区类。配置了一个过滤器,而且有两个分区(不同分区类)引用了该过滤。(依赖注入配置同前一个例子,没必要说了)
2.2 其他配置和上一个例子相似,直接看三个分区效果
对此不想多说了,一切都在以上的热乎乎的栗子里,我的目的已经达到了。你的目的达到了吗?
模块开发是个系统性问题,后续我还将发表使用Mvc分区扩展框架更好的解决实际开发问题文章,敬请期待...
本框架是从搜房内部框架中拆分出来的一部分。随着.net开源社区的快速发展,搜房作为广泛使用.net的大型互联网公司之一,正在逐步以新的姿态拥抱开源。
我们鼓励(甚者奖励)搜房的程序员参与开源项目或者把内部通用系统脱敏整理后在网上开源。
Asp.net Mvc模块化开发之分区扩展框架的更多相关文章
- Asp.net Mvc模块化开发系列(目录)
模块化开发是非常重要的,模块化开发是个系统性问题,为此我觉得有必须要写一个系列的文章才能基本说的清楚 那又为什么要写一个目录呢? 其一.是对我昨天承诺写一个系列新的文章的回应 其二.是先写出一个大纲, ...
- ASP.NET MVC模块化开发——动态挂载外部项目
最近在开发一个MVC框架,开发过程中考虑到以后开发依托于框架的项目,为了框架的维护更新升级,代码肯定要和具体的业务工程分割开来,所以需要解决业务工程挂载在框架工程的问题,MVC与传统的ASP.NET不 ...
- Asp.net Mvc模块化开发之“开启模块开发、调试的简单愉快之旅”
整个世界林林种种,把所有的事情都划分为对立的两个面. 每个人都渴望的财富划分为富有和贫穷,身高被划分为高和矮,身材被划分为胖和瘦,等等. 我们总是感叹,有钱人的生活我不懂;有钱人又何尝能懂我们每天起早 ...
- Asp.net Mvc模块化开发之“部分版本部分模块更新(上线)”
项目开发从来就不是一个简单的问题.更难的问题是维护其他人开发的项目,并且要修改bug.如果原系统有重大问题还需要重构. 怎么重构系统不是本文探讨的问题,但是重构后如何上线部署和本文关系密切.这个大家可 ...
- net Mvc模块化开发
Asp.net Mvc模块化开发之“部分版本部分模块更新(上线)” 项目开发从来就不是一个简单的问题.更难的问题是维护其他人开发的项目,并且要修改bug.如果原系统有重大问题还需要重构. 怎么重构系统 ...
- ASP.NETCORE MVC模块化
ASP.NETCORE MVC模块化编程 前言 记得上一篇博客中跟大家分享的是基于ASP.NETMVC5,实际也就是基于NETFRAMEWORK平台实现的这么一个轻量级插件式框架.那么今天我主要分享的 ...
- 全面解析ASP.NET MVC模块化架构方案
什么叫架构?揭开架构神秘的面纱,无非就是:分层+模块化.任意复杂的架构,你也会发现架构师也就做了这两件事. 本文将会全面的介绍我们团队在模块化设计方面取得的经验.之所以加了“全面”二字,是因为本文的内 ...
- [asp.net mvc 奇淫巧技] 05 - 扩展ScriptBundle,支持混淆加密javascript
一.需求: 在web开发中,经常会处理javascript的一些问题,其中就包括js的压缩,合并,发布版本以及混淆加密等等问题.在asp.net 开发中我们使用ScriptBundle已经可以解决ja ...
- ASP.NET MVC 网站开发总结(三) ——图片截图上传
本着简洁直接,我们就直奔主题吧,这里需要使用到一个网页在线截图插件imgareaselect(请自行下载). 前台页面: <!DOCTYPE html> <html> < ...
随机推荐
- 在AWS中创建NAT节点
NAT, Network Address Translation,即网络地址转换.当内部网络的主机想要访问外网,但是又不想直接暴露给公网,可以通过NAT节点来访问外网.这样做有两个好处,第一是内网的主 ...
- 浅谈敏捷组织中PMO的人物
所谓的"敏捷组织"其实并没有标准的形式,而且PMO(项目办理单位)并没有一个标准的人物界说.有一个十分遍及的误解,公司在挑选"灵敏"或许"瀑布&quo ...
- position格式布局
布局大体分为: 位置--position 绝对坐标 absolute 绝对定位的元素 不受其他位置影响 可通过z-index进行层次分级 body来定位自己 相对坐标 设置 top和left之后 r ...
- C# WebBrowser 获得选中部分的html源码
Winform程序 2.0的. 需要引用Microsoft.mshtml. private void Form1_Load(object sender, EventArgs e) { webBrows ...
- DOM_01之树及遍历
1.DOM:ECMAScript+DOM+BOM,Document Object Model,核心DOM+HTML DOM+XML DOM: 2.辨析:①HTML:专门编写网页内容的语言:②XHTML ...
- 两套JRE
JDK就是Java Development Kit.简单的说JDK是面向开发人员使用的SDK,它提供了Java的开发环境和运行环境. SDK是Software Development Kit 一般指软 ...
- Jetty 服务器的知识
Jetty 服务器的知识 也许你还没有听说过这个Jetty服务器,不过它确实是一种比较轻量级的Java服务器,和Tomcat一样,专门解释JavaWeb程序的服务器.因为在之前学习Java Web 的 ...
- .NET 程序启动调试器 .NET 测试代码耗费时间
有些场景的.NET程序,不容易设置断点,可以用下面的方法,在.NET代码中增加启动调试器的代码: if (!Debugger.IsAttached) Debugger.Launch(); .cshar ...
- HTML5移动Web开发(一)——HTML5和移动网站基本概念以及一些模拟器
当前,手机设备发展迅猛.屏幕尺寸各不相同.存储和性能仍有局限性,如何结合HTML5的功能,在多平台上创建高性能.响应式的移动网站,是Web开发者所要面对的首要挑战.HTML5以及移动网站都是很有前景的 ...
- Sparse Filtering 学习笔记(三)目标函数的建立和求解
Sparse Filtering 是一个用于提取特征的无监督学习算法,与通常特征学习算法试图建模训练数据的分布的做法不同,Sparse Filtering 直接对训练数据的特征分布进行分析,在所谓 ...