为什么需要分离?
我们知道MVC项目各部分职责比较清晰,相比较ASP.NET Webform而言,MVC项目的业务逻辑和页面展现较好地分离开来,这样的做法有许多优点,比如可测试,易扩展等等。但是在实际的开发中,随着项目规模的不断扩大,Controller控制器也随之不断增多。如果在Controllers文件夹下面有超过两位数controller,即便采用良好的命名规范,或者用子文件夹的形式区分不同功能的控制器,还是会影响项目的可阅读性和可维护性。因此,在一些场景下,如果能把与某功能相关的文件分离到一个独立的项目中是非常有用的。譬如网站项目中的前台和后台项目,可以通过此技术分离成两个独立的工程进行开发和维护。
常见方法
分离前台和后台的方法有以下几种不同的实现方式:
- 编写AdminController,并修改Admin路由
- 使用MVC自带的Area机制
- 新建Admin工程,删除Global.asax和Web.config,并添加AdminAreaRegistration;然后将Views通过Post-Build Event拷贝到前台工程的Admin Area的View中
- 使用MvcContrib,将所有静态文件都作为嵌入资源写入Dll文件中,插件式开发
下面两篇文章对上述方法做了详细阐述,此处不做讨论。
NopCommerce的后台分离技术(自定义视图引擎)
本文对NopCommerce的后台分离技术做简单的探讨。NopCommerce通过自定义视图引擎,重写了VirtualPathProviderViewEngine类的CreateView、CreatePartialView、FindView、FindPartialView方法,添加自定义的视图搜索路径来实现后台分离。
说到ASP.NET MVC中的视图引擎(ViewEngine),就不得不说IView和IViewEngine这两个接口,要实现自定义的视图引擎就必须要实现这两个接口:
-
IView接口:IView是对MVC结构中View对象的抽象,此接口只有一个方法:void Render(ViewContext viewContext, TextWriter writer);Render方法将页面HTML写入到Writer中供浏览器显示;
-
IViewEngine接口:IViewEngine接口的职责是寻找View对象,编写自己的视图引擎时可以继承自IViewEngine并重写该类的FindView和FindPartialView方法,这两个方法返回一个ViewEngineResult表示搜索结果。
ASP.NET MVC提供了两个实现了IViewEngine接口的类:VirtualPathProviderViewEngine和WebFormViewEngine。VirtualPathProviderViewEngine类实现了FindView和FindPartialView这两个方法,用于根据指定的路径和格式来搜索页面文件,并且提供了Cache机制来缓存数据(由于使用的是ASP.NET Cache,依赖于HttpContext,所以无法在WebService或WCF项目中使用)。
VirtualPathProviderViewEngine寻找页面的时候,具体从哪些路径下进行寻找其实是根据该类中的这三个属性来决定的:MasterLocationFormats、ViewLocationFormats、PartialViewLocationFormats,修改这三个属性可以给我们的视图引擎分配自定义的搜索路径和文件格式。
对视图引擎的介绍和分析可以参考以下两篇文章:
从零开始学习 ASP.NET MVC 1.0 (五) ViewEngine 深入解析与应用实例
NopCommerce代码分析
NopCommerce定义了自己的试图引擎ThemeableRazorViewEngine,该类的MasterLocationFormats、ViewLocationFormats、PartialViewLocationFormats三个属性指定了寻找视图文件的位置,如下:
ViewLocationFormats = new[] { //themes "~/Themes/{2}/Views/{1}/{0}.cshtml", "~/Themes/{2}/Views/{1}/{0}.vbhtml", "~/Themes/{2}/Views/Shared/{0}.cshtml", "~/Themes/{2}/Views/Shared/{0}.vbhtml", //default "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml", //Admin "~/Administration/Views/{1}/{0}.cshtml", "~/Administration/Views/{1}/{0}.vbhtml", "~/Administration/Views/Shared/{0}.cshtml", "~/Administration/Views/Shared/{0}.vbhtml", };
可以看出除了默认的在View目录下搜索视图以外,NopCommerce还会在Theme目录和Administration目录下搜索视图;Theme目录下的视图用于提供主题机制,此处不作深入讨论;Administration目录就是我们的后台项目的目录,这也是为什么Administration这个工程目录要放在Nop.Web这个目录里面的原因。
NopCommerce没有实现自己的IView接口,ThemeableRazorViewEngine类的CreateView/CreatePartialView直接调用了Razor引擎,如下:
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) { string layoutPath = masterPath; var runViewStartPages = true; IEnumerable<string> fileExtensions = base.FileExtensions; return new RazorView(controllerContext, viewPath, layoutPath, runViewStartPages, fileExtensions); }
ThemeableRazorViewEngine类继承ThemeableBuildManagerViewEngine,ThemeableBuildManagerViewEngine类继承ThemeableVirtualPathProviderViewEngine,ThemeableVirtualPathProviderViewEngine类继承VirtualPathProviderViewEngine,在ThemeableVirtualPathProviderViewEngine类中实现了自定义的搜索视图的方法FindView/FindPartialView,对admin area作了特殊的处理,如下:
//little hack to get nop's admin area to be in /Administration/ instead of /Nop/Admin/ or Areas/Admin/ if (!string.IsNullOrEmpty(areaName) && areaName.Equals("admin", StringComparison.InvariantCultureIgnoreCase)) { //admin area does not support mobile devices if (mobile) { searchedLocations = new string[0]; return string.Empty; } var newLocations = areaLocations.ToList(); newLocations.Insert(0, "~/Administration/Views/{1}/{0}.cshtml"); newLocations.Insert(0, "~/Administration/Views/{1}/{0}.vbhtml"); newLocations.Insert(0, "~/Administration/Views/Shared/{0}.cshtml"); newLocations.Insert(0, "~/Administration/Views/Shared/{0}.vbhtml"); areaLocations = newLocations.ToArray(); }
来源:http://www.iyiruan.com/Blog/Detail?id=42