【问题标题】:MEF and MVC 3 - how to load embedded views dynamically from mef container?MEF 和 MVC 3 - 如何从 mef 容器动态加载嵌入式视图?
【发布时间】:2012-01-07 07:12:05
【问题描述】:

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

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

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

并被放入 Web 应用程序 bin 内的 Plugins 目录中:

  • WebApp/Bin/Plugins/Module1.Data.dll
  • WebApp/Bin/Plugins/Module1.Web.dll
  • WebApp/Bin/Plugins/Module2.Data.dll
  • WebApp/Bin/Plugins/Module2.Web.dll
  • WebApp/Bin/Plugins/ModuleCore.Data.dll
  • WebApp/Bin/Plugins/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并获取视图Index.cshtml)
  2. 获取视图 Index.cshtml 后 - 编译开始(通过 RazorBuildProvider)
  3. 抛出异常:“在命名空间 Module1 中找不到数据类型”,换句话说,在动态构建视图期间找不到 UserDTO

所以编译器/构建器似乎没有通过 bin/Plugins 文件夹查找 Module1.Data.dll,因为当我将此文件复制到 bin 文件夹时 - 它的措辞很好。

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

我已经设法通过创建覆盖标准的 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 文件夹中所有程序集的引用,这可能会在以后使用大量插件时导致性能问题。

有更好的解决方案吗?

【问题讨论】:

  • 您解决了这个问题吗?我目前正在尝试用我的 MVC 应用程序解决同样的问题。您是否有任何正在运行的资源可供我查看?
  • 是的,我如上所述通过 CustomRazorBuildProvider 解决了它。但是,从那时起,我们的应用程序越来越多地使用 MVVM 方法,其中使用较少的 razor 视图,并构建了更多纯 html/javascript 视图。

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


【解决方案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)

让控制器将你的 DTO 映射到 ViewModel

请参阅AutoMapper 以帮助映射

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-25
    • 1970-01-01
    • 2011-03-03
    • 2011-05-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多