标签:
本文内容基于
下文中会提及它们是如何解决问题,在这之前给他们取个小名
cjnmy36723 方案一
BrnShop 方案二
本文不讲代码细节
什么MVC插件
目前我所理解的MVC插件是
我们所想象的插件,是只要把插件文件复制到指定位置,并修改配置就能运行的.
那么就先约定一个Plugins目录来放插件
平时访问MVC网站经过的流程大致为:
Url被路由获取-->路由匹配-->找到相关控制器-->控制器找到相关视图
开发一个插件,并让站点使用会遇到很多坑
那么就从视图,控制器,路由方面 开始解决它们.
控制器问题
如何让网站不仅能拿到网站本身的控制器类,同时能拿到插件的控制器类
那么就要了解下控制器如何被创造出来的
推荐看artech的博客
这里只说相关部分的
通常控制器是被一个控制器工厂类(DefaultControllerFactory)创建出来的
这个类实现了IControllerFactory接口
该接口有三个方法
//使用指定的请求上下文来创建指定的控制器。
IController CreateController(RequestContext requestContext, string controllerName);
//获取控制器的会话行为。
SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName);
//释放指定的控制器
void ReleaseController(IController controller);
获得控制器的关键就是
CreateController方法
只要修改这个方法,让他不仅仅能获得网站自己的控制器类,同时能获得插件的控制器类
当然我们修改的话,并不需要自己实现IControllerFactory创建自己的控制器工厂类,我们可以重写DefaultControllerFactory相关的方法
DefaultControllerFactory除了能重写IControllerFactory的三个方法外还能重写
-
protected internal virtual TypeGetControllerType(RequestContext requestContext,string controllerName)
为了达到我们的目的可以重写这个方法,因为根据源码,CreateController创建实例需要Type,即会用到GetControllerType方法
最简单的处理:可以重写这个方法,让它反射程序集,创建控制器
下面介绍下两个案例是如何处理控制器的
部署程序集
显然无论哪种方法都需要加载程序集,这里需要注意的是两种方案都把程序集复制到另一个文件夹下
方案一
复制到 ~/App_Data/Dependencies
方案二
它略微复杂点,它尝试把插件的dll复制到运行时文件夹
临时文件夹大概在C:\Users\用户名\AppData\Local\Temp\Temporary ASP.NET Files
这里就涉及到了权限问题
在BrnShop的BrnMall.Core.BMAPlugin.DeployDllFile方法中它判断应用程序的信任级别,如果是最高等级的信任级别就把插件dll复制到运行时文件夹.如果不是则复制到/plugins/bin.
之所以要复制程序集,因为使用Assembly.LoadFrom方法后,将把该程序集锁定,那么久不能移动,删除程序集了
这里我无意间在网上看到了一个解决方法.把dll以字节的形式读取到内存,再生成Assembly,这样似乎锁程序集
但我仍然有个疑惑,为什么BrnShop要尝试把程序集复制到运行时文件夹?归在一个文件夹不好吗?求解答
使用程序集
部署好程序集后,就要想办法,让网站能用到这个程序集
方案一
方案二
BrnShop并不是像上面那样运行时反射加载程序集并保存起来用
确切的说他是运行前加载程序集并添加引用
在它的BrnMall.Core.BMAPlugin.DeployDllFile 方法中,它读取插件程序集并使用BuildManager.AddReferencedAssembly添加程序集引用.
这里我说下相关部分
CLR会按照下面的顺序搜索程序集,但提前这个程序集要被引用,只有被引用的程序集才会出现在程序集清单中,CLR搜索程序集就是靠清单上的信息
1、 在GAC(Global Assembly Cache)中搜索相应版本的DLL
2、 配置文件(web.config或app.config)中
<codebaseversion=”AssemblyVersion" href=”URL of assembly” />
3、 应用程序当前目录下
4、 配置文件(web.config或app.config)
<probingprivatePath=”Paths”/>
所以Brnshop不仅动态添加了引用,同时设置了<probing>节点
-
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<!--插件dll文件影子目录,在Medium及其以下的信任级别时使用-->
<probing privatePath="plugins/bin/"/>
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="3.0.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
这样做后,网站就能想找自己控制器一样轻松的找到插件的控制器
存在问题,使用这种方法,如果在网站运行时添加插件,毕竟运行的情况下是不同添加引用的
视图问题
这里要了解想MVC的Razor视图是如何被编译出来的
推荐看artech的MVC视图系列文章
http://www.cnblogs.com/artech/archive/2012/09/04/razor-view-engine-01.html
这里也只说相关部分的
找视图
视图引擎RazorViewEngine的爷爷
VirtualPathProviderViewEngine(抽象类)
它内部有一些数组用于保存视图的路径
-
public string[] AreaMasterLocationFormats{ get; set; }
public string[] AreaPartialViewLocationFormats{ get; set; }
public string[] AreaViewLocationFormats{ get; set; }
public string[] FileExtensions{ get; set; }
public string[] MasterLocationFormats{ get; set; }
public string[] PartialViewLocationFormats{ get; set; }
public IViewLocationCache ViewLocationCache{ get; set; }
public string[] ViewLocationFormats{ get; set; }
显然根据名字就知道,它们是哪些视图的路径
它们在RazorViewEngine中初始化,下面给出一部分的代码
-
public RazorViewEngine(IViewPageActivator viewPageActivator) : base(viewPageActivator)
{
//省略....
base.ViewLocationFormats = new string[]
{
"~/Views/{1}/{0}.cshtml",
"~/Views/{1}/{0}.vbhtml",
"~/Views/Shared/{0}.cshtml",
"~/Views/Shared/{0}.vbhtml"
};
//省略...
}
上面的路径仅仅是个模板,真正的路径将在运行时候根据路由请求被修改
这部分操作被写在 VirtualPathProviderViewEngine中的
-
public virtual ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
...
}
易知,这个方法可以被重写,那么我们可以修改FindView方法和ViewLocationFormats等数组,让视图引擎能找到插件的视图
编译视图
如果你看过artech的文章,应该明白,视图(.cshtml)将在运行的时候编译成一个类保存在运行时文件夹
临时文件夹大概在
C:\Users\用户名\AppData\Local\Temp\Temporary ASP.NET Files
那么这里就出现了一个 坑
由于是把视图文件(.cshtml)编译成一个dll,那么当这个视图是模型视图,也就是用到了自己创建的Model类时候该去哪个dll找我的Model?
通常情况下,我写的Model都跟我的MVC网站编译成一个dll
比如
我的网站将编译成一个PluginMvcWeb.dll保存在bin下,运行时也会保存在临时文件夹下.
方案一
他也设置了<probing>节点,告诉CLR去哪里搜索dll
可能有人问,这个视图类还没引用程序集,我写到这也惊得菊花一紧.然而神奇的是这视图类会自动添加引用
方案二
它是写死视图的路径(毕竟是约定大于配置的项目),同样也设置了<probing>节点
路由
方案一
每个插件加载时候都会调用一个初始化函数,在这个函数中每个插件都会注册一条路由
方案二
额.......,它似乎并没改动什么,它是约定大于配置的,主网站默认的路由配置已经能够匹配到插件的位置
未完待撸...
MVC插件开发学习
标签:
原文地址:http://www.cnblogs.com/Recoding/p/5294833.html