开发一个在Razor mvc下的插件(model/view)

我最近花了很多很多时间研究和原型不同的方式在ASP.NET MVC3创建一个插件引擎,主要是找到一个很好的方式来加载'bin'文件夹以外的插件(DLL)。虽然这个研究集中在MVC3,我相信,同样的原则将适用于其他版本的MVC。

 

问题

从'bin'文件夹外面的目录中载入的DLL并不是真的非常新的或尖端的技术,然而,当与MVC的工作结合就变得更加困难。这主要是由于MVC如何加载/查找类型的,它需要处理,包括控制器,视图模型(更准确地说是传递的范型ViewPage或用@model声明在Razor 中的泛型参数),MVC是非常深度依赖BuildManager,buildmanager负责编译View,寻找其他服务,如控制器定位机制。默认情况下,BuildManager只与'bin'的文件夹下的assembies熟悉,以及在GAC中的,所以如果你开始把DLL放到'bin'以外的文件夹中的,那么它将无法找到MVC的服务和对象可能要到引用。

需要处理的另一个问题是DLL文件锁定。当一个插件DLL被加载和处于使用中,CLR将锁定该文件。这将成为一个问题,如果开发人员要更新的插件DLL,而网站正在运行,因为它们将无法除非他们破坏web.config或采取暂时关闭网站的行为。一直以来,使用MEF的框架,它也是如此加载的DLL(需要关闭网站)。

 

NET4的新功能之一是在程序初始化前执行代码的能力,应用程序预初始化给BuildManager带来灵活的空间,让你在运行时添加程序集引用(必须完成预初始化应用)的新功能。这里是菲尔哈克这些新功能的一个不错的参考:http://haacked.com/archive/2010/05/16/three-hidden-extensibility-gems-in-asp-net-4.aspx

To use this attribute, create a class library and addthis attribute as an assembly level attribute. A common place to add this wouldbe in the AssemblyInfo.cs class within the Properties folder.

[assembly: PreApplicationStartMethod(

  typeof(SomeClassLib.Initializer),"Initialize")]

这是必不可少的插件框架与MVC工作BuildManager知道哪里引用以外的'bin',你的插件的DLL。然而,这并不是故事的结尾。

Stronglytyped Views with model Types located in plugin DLLs

不幸的是,如果你有一个视图关联到存在的'bin'目录之外的Assembly的强类型的模型,然后你会发现,它不工作,而且实际上并不会告诉你为什么,出错信息无意义。这是因为RazorViewEngine使用视图来自BuildManager编译成的动态assembly,然后使用Activator.CreateInstance实例化新编译的对象。这就是问题所在,当前AppDomain不知道如何解决,因为在'bin'或GAC下不存在强类型的视图。有关此方案的部分更糟糕的是,你没有得到任何错误信息,告诉你这是为什么不工作,或者是哪里出了问题。相反,你得到提示是“MVC视图没有发现”的错误:“......它的Master没有被发现或没有视图引擎支持搜索的位置。下列地点进行了全面搜查......“告诉你,它已搜索在所有的ViewEngine地点的View,并无法找到它......这实际上是不是所有的错误。深入学习 MVC3源码 ,它试图实例化视图对象的动态assembly和然后失败,所以它只是不断寻找,其余的ViewEngine路径,而不是去APPdomain下所有已加载的Type。

关于解决以上问题,分两个部分:

Full trust/Medium trust

 

当我们在fulltrust的情况下,有几种方式可以解决上述解析type不搜索APPdomain已Load的正常assembly,只搜索动态编译的路径下的Assembly.

1. 最直接的,就是hookAPPDomain的ResolveAssemblyEvent.通过监听事件,能够通知AppDomain到哪里去加载type。

static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgsargs)

{

    var pluginsFolder = newDirectoryInfo(HostingEnvironment.MapPath("~/Plugins"));

    return (from f in pluginsFolder.GetFiles("*.dll", SearchOption.AllDirectories)

            let assemblyName =AssemblyName.GetAssemblyName(f.FullName)

//检查plugin assembly的名字是否和寻找目标一致,是就直接返回

            where assemblyName.FullName == args.Name ||assemblyName.FullName.Split(',')[0] == args.Name

            select Assembly.LoadFile(f.FullName)).FirstOrDefault();

}

2.也很直接,在动态编译输出目录下准备好需要的assembly.

AppDomain’s DynamicDirectory. 是Buildmanager编译输出的地方,也是后期Appdomain解析Type的时候要load的assembly所在地

通过程序预初始化阶段把文件拷贝过去。

The burden of Medium Trust

在MVC世界里,在“完全信任”应用场景下,'bin'目录以外的插件的加载只有以上的一个障碍。在中等信任APP domain,是个有趣的事情。不幸的是,在中等信任情况下,既不可能处理AssemblyResolve事件,也不可能访问的AppDomain DynamicDirectory,因此上述两个解决方案抛到了九霄云外。这个时候,似乎只有使用CodeDOM自定义编译View了。

DLLfile locking

DLL文件锁定

由于插件的DLL得到由CLR锁定在加载时,我们需要解决这个问题。解决的办法是预初始化应用的另一个文件夹的卷影副本的DLL。如前所述,这是获得插件加载在“完全信任”,在我看来是最好的方式做到这一点,因为它杀死一石二鸟的方法之一。然而在中等信任,我们将不得不通过跳转到一个临时文件夹在Web应用程序中存在的一些篮球和卷影副本的DLL。重要的一点是:当你复制,你可能会增加一个版本号或类似的DLL的名称修改的DLL,但是这不会工作,你就会得到一个“位于集的清单定义...不匹配组装参考。“错误。

基本上来看在Medium Trust下无法实现的。

Solution

UPDATE: The latest version of this code can be found in theUmbraco v5 source code. The following code does work but there’s been a lot ofenhancements to it in the Umbraco core. Here’s the latest changeset as of28/10/2011 Umbraco v5 PluginManager.cs

Working in FullTrust, the simplest solution is to shadow copy your plugin DLLs into yourAppDomain DynamicDirectory. Working in Medium Trust you’ll need to do thefollowing:

  • On application pre-init:
    •  
    • Shadow copy all of your      plugin DLLs to a temporary folder in your web application (not in the      ‘bin’)  
    • Add all of the copied      DLLs to be referenced by the BuildManager
  • Add all folder paths to     the privatePath     attribute of the probing     element in your web.config to point to where you will be copying your DLLs    
    •  
    • If you have more than      one, you need to semi-colon separate them

建议原文:

     http://haacked.com/archive/2010/05/16/three-hidden-extensibility-gems-in-asp-net-4.aspx

     

    评论 1
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值