11

我正在構建一個使用MEF的MVC 3應用程序。主要思想是在mef容器的運行期間有模型,控制器和視圖動態加載的插件機制。MEF和MVC 3 - 如何從mef容器動態加載嵌入視圖?

每個插件/模塊包括兩個組件:

  • Module1.Data.dll(包含的模型定義)
  • Module1.Web.dll(包含控制器和視圖)

並放在Web應用程序bin內的Plugins目錄中:

  • WebApp/Bin/Plugins/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()); 

好了,所以處理可插拔模型,控制器和視圖的整個基礎設施已經準備就緒。現在一切正常...除了一件事 - 強烈鍵入意見

要ilustrate的問題更多的細節,讓我們準備的一幕:

  • UserDTO模型位於Module1.Data.dll
  • ShowUserController.cs位於Module1.Web.dll /控制器/
  • Index.cshtml位於Module1.Web.dll/Views/ShowUser(聲明爲@model Module1.Data。UserDto)

現在我們做到以下幾點:

  1. 運行應用程序並進入主機/ ShowUser /指數(action方法指數上ShowUserController執行並查看Index.cshtml被取出)
  2. 在獲取視圖Index.cshtml之後 - 編譯開始(由RazorBuildProvider提供)
  3. 引發異常:「在命名空間Module1中找不到數據類型」,換句話說在動態構建視圖期間無法找到UserDTO

因此,似乎編譯器/生成器沒有通過Module1.Data.dll的bin/Plugins文件夾查看,因爲當我將此文件複製到bin文件夾中時 - 它措辭良好。

問題:爲什麼builder沒有查看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文件夾中的所有程序集的引用需要被添加,這會在將來會使用大量插件時導致性能問題。

任何更好的解決方案?

+2

你有沒有得到這個解決?我現在正試圖解決與我的MVC應用程序相同的問題。你有沒有任何資源正在運行,我可能會看看? – Coppermill

+1

是的,我通過CustomRazorBuildProvider解決了上述問題。 Hovewer從那時起,我們的應用程序越來越多地使用MVVM方法,在這種方法中使用較少的剃刀視圖,並且構建更純淨的html/javascript視圖。 – untoldex

回答

1

這是一個想法。

如果您遵循視圖模型模式,則不是直接將DTO的視圖模型發送到視圖,而是使用位於與視圖相同的程序集中的ViewModel。

所以不是:

UserDTO模型位於Module1.Data.dll ShowUserController.cs位於Module1.Web.dll /控制器/ Index.cshtml位於Module1.Web.dll /查看/ ShowUser(與@model Module1.Data.UserDto聲明)

你必須:

UserDTO模型位於Module1.Data.dll ShowUserController.cs位於Module1.Web.dll /控制器/ 位於Module1.Web.d中的UserVM LL /的ViewModels Index.cshtml位於Module1.Web.dll /瀏覽/ ShowUser(已宣告@model Module1.Web.ViewModels.UserVM)

有控制地圖的DTO的到的ViewModels

AutoMapper幫助製圖