用過和作過插件的都會了解插件的好處,園子裏也有不少和討論,但大都只些簡單的加載程序集什麼的,這裏主要討論的就是使用 ASP.NET MVC 4 來實現每一個插件均可以徹底從主站點剝離出來,即便只是一個插件,也是一個完整的站點,同時也能夠和其它插件一塊兒組裝成一個龐大的系統。html
參考資料:前端
ASP.NET MVC 4 源碼。架構
Orchard 源碼。併發
MVC3PlugInDemo 源碼。mvc
http://stackoverflow.com/questions/6923572/asp-net-mvc-3-portable-area-view-doesnt-find-my-modelide
首先,很是感謝以上幾位大牛分享的文章,因爲文筆很差,對.NET 瞭解也不夠,但願你們多多指點。模塊化
理想狀況下是但願可以像Orchard那樣,能夠運行時修改代碼,又或者能夠直接把插件(包含頁面、樣式、圖片等資源文件)編譯成一個DLL來使用(可是這樣的作法會對前端與美工修改不便,並且就算改一個字也要從新編譯一個DLL),只是依然還沒找到方法ORZ。post
最終結構圖以下。ui
當把插件的站點發布出來後,目錄名爲插件名,並將該目錄及目錄下的全部文件複製到Plugins目錄下便可自動安裝並運行,不須要重啓程序池。google
要實現這麼一個架構,最初認爲,只要使用 Assembly.LoadFile(name);方法來加載外部的程序集,而且使用反射建立控制器,在定義一下MVC的模板引擎的搜索路徑不就能夠了嗎?
當具體實現以後,發現,在不使用強類型的模型綁定時,能夠正常使用,可是,使用了強類型的模型綁定時,則會出現如下錯誤。
問題產生緣由:
.NET 會把.cshtml 與相關的程序集進行編譯,以後訪問的是編譯後的臨時程序集,可是,因爲沒有引用進入系統中,這裏編譯的時候沒有該程序集,就會出現錯誤。
那麼,要解決這個問題,就須要在編譯時,把須要的程序集,都一塊兒編譯了,可是,怎麼樣才能夠實現?
直接引用並使用類庫的時候,系統會自行編譯到一塊兒了,因此解決辦法有兩種:
一、在系統啓動前的預編譯時,手動把相關的程序集增長進系統中,這樣就是一個實際存在於系統的程序集,在頁面編譯時天然會編譯進去。
在google查找相關的解決方法時,發現了該方法:
BuildManager.AddReferencedAssembly(assembly);
在查MSDN有這麼一段話:此方法必須在 Global.asax 文件中的 Application_Start 事件發生前調用。
也就意味着加載程序集的方法就必需要在預啓動階段就是加載了。
而且使用上面的方法,來把程序集加到系統裏。
雖然這樣能夠正常使用了,可是,偶爾仍是會有出現編譯錯誤的異常。
在調試階段下,只有從新生成的代碼時能夠正常運行,從新生成以後的代碼,在點啓動調試時,就會出現編譯錯誤問題,調試發現,在這個時候,系統並無將須要的程序集加載到系統中,有大牛瞭解的話但願指點下緣由。
可是,在使用了Web.config 配置文件中的節點「probing」之後,把相關的程序集複製到「probing」指定的目錄下,就能正常運行了。
可是, 因爲上面的那段代碼只能夠在預啓動階段使用,全部註定了該方法有個缺點,就是每一個更新插件時,都要重啓或者回收一次程序池,沒能正常的作到插件化的靈活性。
二、在模板(cshtml)進行編譯前,把外部引用的相關的程序集增長到編譯信息中,這樣在對模板進行編譯的同時,會把該程序集也編譯進去。
在谷歌孃的幫助下,找到了該事件:
RazorBuildProvider.CodeGenerationStarted
從名字能夠看出,這是在編譯啓動時觸發的事件,具體功能不明ORZ。
能夠經過該事件,把外部的程序集增長到 RazorBuildProvider 類中。
provider.AssemblyBuilder.AddAssemblyReference(plugin.Assembly);
provider 是 RazorBuildProvider 類的一個實例。
plugin.Assembly 是一個頁面所使用的程序集。
只要把這段代碼放到模板引擎的搜索視圖的位置,便可根據須要,將增長外部的程序集,因爲重複增長會出現已添加組件異常,因此,這裏加了個 isLoadAssembly 變量來確認是否已增長過。
/// <summary> /// 給運行時編譯的頁面加了引用程序集。 /// </summary> /// <param name="pluginName"></param> private void CodeGeneration(string pluginName) { RazorBuildProvider.CodeGenerationStarted += (object sender, EventArgs e) => { RazorBuildProvider provider = (RazorBuildProvider)sender; var plugin = PluginManager.GetPlugin(pluginName); if (plugin != null) { provider.AssemblyBuilder.AddAssemblyReference(plugin.Assembly); } }; }
我不喜歡每裝一個插件,都要重啓一次,因此我選擇使用了第二種方法。
下一篇,將使用第二種方法來進行具體的實踐併發布源碼。