对于一个企业级项目开发,模块化是非常重要的。

默认Mvc框架的AreaRegistration对模块化开发真的支持很好吗?真的有很多复杂系统在使用默认的分区开发的吗?我相信大部分asp.net的技术团队最开始都研究过分区,甚至在实际项目里面有尝试运用,但是碰到了种种问题"各种坑",最后回头是岸放弃了(我们的团队也碰到了类似问题,也有人评论中说起,直接搜索asp.net mvc分区也有不少类似信息)。

有人说asp.net Mvc框架就不适合做模块化开发,我们可以弄一个其他框架来做企业级的分模块开发,确实现在好像已经有类似的开源项目。

但是,这个确实没有必要,这里对Mvc框架做了一个简单的扩展,基本能做到分模块开发和单个简单Mvc项目开发没有太大区别,并分享给大家。

本文中的栗子是使用.net4.0、Mvc4.0及Unity2.0(企业库4.0)的。

本分区扩展集成了IoC和分区DI(依赖注入)及分区过滤器的支持。

感兴趣的同学请继续,用AreaRegistration有不爽的看官请拭目以待...


Asp.net Mvc模块化开发系列目录

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模块化开发之分区扩展框架的更多相关文章

  1. Asp.net Mvc模块化开发系列(目录)

    模块化开发是非常重要的,模块化开发是个系统性问题,为此我觉得有必须要写一个系列的文章才能基本说的清楚 那又为什么要写一个目录呢? 其一.是对我昨天承诺写一个系列新的文章的回应 其二.是先写出一个大纲, ...

  2. ASP.NET MVC模块化开发——动态挂载外部项目

    最近在开发一个MVC框架,开发过程中考虑到以后开发依托于框架的项目,为了框架的维护更新升级,代码肯定要和具体的业务工程分割开来,所以需要解决业务工程挂载在框架工程的问题,MVC与传统的ASP.NET不 ...

  3. Asp.net Mvc模块化开发之“开启模块开发、调试的简单愉快之旅”

    整个世界林林种种,把所有的事情都划分为对立的两个面. 每个人都渴望的财富划分为富有和贫穷,身高被划分为高和矮,身材被划分为胖和瘦,等等. 我们总是感叹,有钱人的生活我不懂;有钱人又何尝能懂我们每天起早 ...

  4. Asp.net Mvc模块化开发之“部分版本部分模块更新(上线)”

    项目开发从来就不是一个简单的问题.更难的问题是维护其他人开发的项目,并且要修改bug.如果原系统有重大问题还需要重构. 怎么重构系统不是本文探讨的问题,但是重构后如何上线部署和本文关系密切.这个大家可 ...

  5. net Mvc模块化开发

    Asp.net Mvc模块化开发之“部分版本部分模块更新(上线)” 项目开发从来就不是一个简单的问题.更难的问题是维护其他人开发的项目,并且要修改bug.如果原系统有重大问题还需要重构. 怎么重构系统 ...

  6. ASP.NETCORE MVC模块化

    ASP.NETCORE MVC模块化编程 前言 记得上一篇博客中跟大家分享的是基于ASP.NETMVC5,实际也就是基于NETFRAMEWORK平台实现的这么一个轻量级插件式框架.那么今天我主要分享的 ...

  7. 全面解析ASP.NET MVC模块化架构方案

    什么叫架构?揭开架构神秘的面纱,无非就是:分层+模块化.任意复杂的架构,你也会发现架构师也就做了这两件事. 本文将会全面的介绍我们团队在模块化设计方面取得的经验.之所以加了“全面”二字,是因为本文的内 ...

  8. [asp.net mvc 奇淫巧技] 05 - 扩展ScriptBundle,支持混淆加密javascript

    一.需求: 在web开发中,经常会处理javascript的一些问题,其中就包括js的压缩,合并,发布版本以及混淆加密等等问题.在asp.net 开发中我们使用ScriptBundle已经可以解决ja ...

  9. ASP.NET MVC 网站开发总结(三) ——图片截图上传

    本着简洁直接,我们就直奔主题吧,这里需要使用到一个网页在线截图插件imgareaselect(请自行下载). 前台页面: <!DOCTYPE html> <html> < ...

随机推荐

  1. openseadragon.js与deep zoom java实现艺术品图片展示

    openseadragon.js 是一款用来做图像缩放的插件,它可以用来做图片展示,做展示的插件很多,也很优秀,但大多数都解决不了图片尺寸过大的问题. 艺术品图像展示就是最简单的例子,展示此类图片一般 ...

  2. [我给Unity官方视频教程做中文字幕]beginner Graphics – Lessons系列之摄像机介绍Cameras

    [我给Unity官方视频教程做中文字幕]beginner Graphics – Lessons系列之摄像机介绍Cameras 最近得到一些Unity官方视频教程,一看全是纯英文的讲解,没有任何字幕或者 ...

  3. (翻译)正确实施DevOps-The Lay of the Land

    原文地址:http://www.drdobbs.com/architecture-and-design/getting-devops-right-the-lay-of-the-land/2400626 ...

  4. <HTML5和CSS3响应式WEB设计指南>译者序

    "不是我不明白,这世界变化快."崔健的这首歌使用在互联网领域最合适不过.只短短数年的功夫,互联网的浪潮还没过去,移动互联网的时代已经来临.人们已经习惯将越来越多的时间花在各种移动设 ...

  5. jmap

    环境: 现有一个独立运行的系统S(有独立的jre,但是没jdk),现想通过jmap导出其内存堆栈信息.于是另外安装一个jdk.可是jdk的版本跟S系统的jre不能对应上.出了很多错误. 总是报错: C ...

  6. Atitit   图像处理 平滑 也称 模糊, 归一化块滤波、高斯滤波、中值滤波、双边滤波)

    Atitit   图像处理 平滑 也称 模糊, 归一化块滤波.高斯滤波.中值滤波.双边滤波) 是一项简单且使用频率很高的图像处理方法 用途 去噪 去雾 各种线性滤波器对图像进行平滑处理,相关OpenC ...

  7. Atitit 图像处理之理解卷积attilax总结

    Atitit 图像处理之理解卷积attilax总结 卷积的运算可以分为反转.平移,相乘,求和.        在图像处理中,图像是一个大矩阵,卷积模板是一个小矩阵.按照上述过程,就是先把小矩阵反转,然 ...

  8. Atitit 基于dom的游戏引擎

    Atitit 基于dom的游戏引擎 1. 添加sprite控件(cocos,createjs,dom)1 1.1.1. Cocos1 1.1.2. createjs1 1.1.3. Dom模式2 1. ...

  9. Android中的Activity相关知识总结

    一.什么是Activity? 简单理解:Activity是Android组件中最基本也是最为常见用的四大组件之一.是一个与用户交互的系统模块,一个Activity通常就是一个单独的屏幕(页面), 它上 ...

  10. [BUG集] android 安卓项目中ORMLITE框架 Must specify one of id, generatedId, and generatedIdSequence with Id

    使用ORM框架ORMLITE有一段时间,今天在操作一个对象的时候,重新运行报错如下: Must specify one of id, generatedId, and generatedIdSeque ...