MEF和MVC 3 - 如何从mef容器动态加载嵌入式视图?

时间:2011-11-27 12:16:39

标签: asp.net-mvc-3 razor mef strongly-typed-view dynamic-compilation

我正在构建一个使用MEF的MVC 3应用程序。主要思想是拥有插件机制,其中模型,控制器和视图在运行时从mef容器动态加载。

每个插件/模块由两个程序集组成:

  • Module1.Data.dll(包含模型的定义)
  • Module1.Web.dll(包含控制器和视图)

并放在Web应用程序bin中的Plugins目录中:

  • Web应用程序/滨/插件/ Module1.Data.dll
  • Web应用程序/滨/插件/ Module1.Web.dll
  • Web应用程序/滨/插件/ Module2.Data.dll
  • Web应用程序/滨/插件/ Module2.Web.dll
  • Web应用程序/滨/插件/ ModuleCore.Data.dll
  • Web应用程序/滨/插件/ ModuleCore.Web.dll
  • 等...

还有所有其他模块引用的核心模块:ModuleCore.Data.dll和ModuleCore.Web.dll。

然后,在Global.asax中,容器按以下方式构建:

AggregateCatalog catalog = new AggregateCatalog();
var binCatalog = new DirectoryCatalog(HttpRuntime.BinDirectory, "Module*.dll");
var pluginsCatalot = new DirectoryCatalog(Path.Combine(HttpRuntime.BinDirectory, "Plugins"), "Module*.dll");
catalog.Catalogs.Add(binCatalog);
catalog.Catalogs.Add(pluginsCatalot);
CompositionContainer container = new CompositionContainer(catalog);
container.ComposeParts(this);
AppDomain.CurrentDomain.AppendPrivatePath(Path.Combine(HttpRuntime.BinDirectory, "Plugins"));

CustomViewEngine已创建并注册并用于模块组装中的查找视图

ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new CustomViewEngine());

控制器工厂,用于从容器加载控制器:

ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(_container));

以及用于从容器中获取程序集的自定义虚拟路径提供程序:

HostingEnvironment.RegisterVirtualPathProvider(new ModuleVirtualPathProvider());

好的,因此处理可插拔模型,控制器和视图的整个基础架构已准备就绪。现在一切正常......除了一件事 - 强类型视图

为了更详细地说明问题,让我们准备场景:

  • UserDTO模型位于Module1.Data.dll
  • ShowUserController.cs位于Module1.Web.dll / Controllers /
  • Index.cshtml位于Module1.Web.dll / Views / ShowUser(声明为@model Module1.Data.UserDto)

现在我们执行以下操作:

  1. 运行应用程序并转到HOST / ShowUser / Index(操作方法索引在ShowUserController上执行并查看Index.cshtml被提取)
  2. 获取视图Index.cshtml后 - 编译开始(通过RazorBuildProvider)
  3. 抛出异常:“找不到名称空间Module1中的数据类型”,换句话说,在动态构建视图期间找不到UserDTO
  4. 所以似乎编译器/构建器没有查看Module1.Data.dll的bin / Plugins文件夹,因为当我将这个文件复制到bin文件夹时 - 措辞很好。

    问题/问题:为什么构建器没有查看bin / Plugins文件夹,即使此目录是由AppDomain.CurrentDomain.AppendPrivatePath方法添加的? 如何为程序集生成器添加一次私有路径,以便考虑插件文件夹?

    我设法通过创建覆盖标准的CustomRazorBuildProvider来做一些工作:

    public class CustomRazorBuildProvider : RazorBuildProvider
    {
      public override void GenerateCode(System.Web.Compilation.AssemblyBuilder assemblyBuilder)
      {
        Assembly a = Assembly.LoadFrom(Path.Combine(HttpRuntime.BinDirectory, "Plugins", "Module1.Data.dll"));
        assemblyBuilder.AddAssemblyReference(a);      
        base.GenerateCode(assemblyBuilder);
      }
    } 
    

    但是这个解决方案的缺点是每次编译视图时,都需要添加对Plugins文件夹中所有程序集的引用,这可能会在以后使用大量插件时导致性能问题。

    任何更好的解决方案?

1 个答案:

答案 0 :(得分:1)

这是一个想法。

如果您遵循视图模型模式,那么不要将DTO直接发送到视图,而是使用与视图位于同一程序集中的ViewModel。

所以不要:

UserDTO模型位于Module1.Data.dll中 ShowUserController.cs位于Module1.Web.dll / Controllers / Index.cshtml位于Module1.Web.dll / Views / ShowUser(声明为@model Module1.Data.UserDto)

你会:

UserDTO模型位于Module1.Data.dll中 ShowUserController.cs位于Module1.Web.dll / Controllers / UserVM位于Module1.Web.dll / ViewModels中 Index.cshtml位于Module1.Web.dll / Views / ShowUser(声明为@model Module1.Web.ViewModels.UserVM)

让Controller将您的DTO映射到ViewModels

请参阅AutoMapper以帮助进行制图