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

默认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. Hello Mybatis 02 mybatis generator

    接着上一篇文章通过Mybatis完成了一个User的CRUD的功能之后,这篇开始还需要建立一个Blog类,这样就可以模拟一个简单的微博平台的数据库了. 数据库准备 首先我们,还是需要在数据库中新建一个 ...

  2. Linux常用命令(转)

    源自:http://www.linuxidc.com/Linux/2011-08/40437.htm Linux管理文件和目录的命令 命令 功能 命令 功能 pwd 显示当前目录 ls 查看目录下的内 ...

  3. 我心中的核心组件(可插拔的AOP)~第五回 消息组件

    回到目录 之所以把发消息拿出来,完全是因为微软的orchard项目,在这个项目里,将公用的与领域无关的功能模块进行抽象,形成了一个个的组件,这些组件通过引用和注入的方式进行工作,感觉对于应用程序的扩展 ...

  4. 将不确定变成确定~Uri文本文件不用浏览器自动打开,而是下载到本地

    回到目录 这个标题有点长,简单来说就是,对于一个文件下载来说,是否可以提示用户,让它去保存,而不是将它在浏览器中打开,在浏览器中打开有个致命问题,那就是,如果你的页面编码和文件的编码不一致时,打开的就 ...

  5. SSM 三大框架整合

    上一篇已经讲了整个各个子模块的创建过程以及它们之间的依存关系, 那么这一篇就来正式的整合三大框架(SSM)了. 1, 准备环境1.1 为每个War包工程创建一个Server 那么 添加了Server后 ...

  6. Atitit.软件与编程语言中的锁机制原理attilax总结

    Atitit.软件与编程语言中的锁机制原理attilax总结 1. 用途 (Db,业务数据加锁,并发操作加锁.1 2. 锁得类型 排它锁 "互斥锁 共享锁 乐观锁与悲观锁1 2.1. 自旋锁 ...

  7. CCNA网络工程师学习进程(1)网络的基本概述

        在互联网快速发展的今天,了解互联网成为一项必须的技能,因此在学习编程之余学习如何配置网络还是很有必要的. 本系列博客计划分为三个部分,包括思科CCNA.CCNP和华为的网络工程师认证有关的知识 ...

  8. Android Studio下载及使用教程(转载)

    (一)下载及相关问题解决: Android Studio 下载地址,目前最新可下载地址,尽量使用下载工具. Android Studio正式发布,给Android开发者带来了不小的惊喜.但是下载地址却 ...

  9. 谈谈java的运行机制

    1.高级语言的运行机制 我们编程都是用的高级语言(写汇编和机器语言的大牛们除外),计算机不能直接理解高级语言,只能理解和运行机器语言,所以必须要把高级语言翻译成机器语言,计算机才能运行高级语言所编写的 ...

  10. JAVA设计模式《四》

    经过前几篇的介绍相信大家对JAVA的设计模式一定有所解了,本篇我们再一起学习一下适配器模式.代理模式和工厂模式. 适配器模式使用的场景非常多,例如现实生活中,我们的笔记本电脑的充电线大部分都是三向插头 ...