使用ASP.NET中MVC引擎開發外掛程式系統的樣本詳解

來源:互聯網
上載者:User
這篇文章主要為大家詳細介紹了使用ASP.NET MVC引擎開發外掛程式系統的相關資料,具有一定的參考價值,感興趣的小夥伴們可以參考一下

一、前言

我心中的外掛程式系統應該是像Nop那樣(更牛逼的如Orchard,OSGI.NET),每個外掛程式模組不只是一堆實現了某個業務介面的dll,然後採用反射或IOC技術來調用,而是一個完整的mvc小應用,我可以在後台控制外掛程式的安裝和禁用,目錄結構如下:

產生後放在網站根目錄下的Plugins檔案夾中,每個外掛程式有一個子檔案夾

Plugins/Sms.AliYun/

Plugins/Sms.ManDao/

我是一個有強迫症的的懶人,我不想將產生的dll檔案拷貝到bin目錄。

二、要解決的問題

1.asp.net引擎預設只會載入“bin”檔案夾中的dll,而我們想要的外掛程式檔案則是分散在Plugins目錄下的各個子目錄中。

2.視圖中使用了模型時如何處理?預設情況下RazorViewEngine使用BuildManager將視圖編譯成動態程式集,然後使用Activator.CreateInstance執行個體化新編譯的對象,而使用外掛程式dll時,當前的AppDomain不知道如何解析這種引用了模型的視圖,因為它不存在於“bin”或GAC中。更糟糕的是,不會收到任何錯誤訊息,告訴您為什麼它不工作,或者問題在哪。相反,他會告訴你,從View目錄中找不到檔案。

3.某個外掛程式正掛在網站下運行著,直接覆蓋外掛程式的dll,會告訴你當前dll正在使用,不能被覆蓋。

4.視圖檔案不放網站的View目錄中,該如何載入。

三.Net 4.0讓這一切變成可能

Net4.0有一個新特性是在應用程式初始化之前執行代碼的能力(PreApplicationStartMethodAttribute),這個特性使得應用程式在Application_Star前可以做一些工作,例如我們可以在應用啟動之前告知我們的mvc外掛程式系統的dll放在哪,做預先載入處理等。關於.net的幾個新特性,有歪果仁寫得有部落格來介紹,點我。,關於PreApplicationStartMethodAttribute,有博友已經寫過了,點我。 Abp的啟動模組應該也是使用PreApplicationStartMethodAttribute這個特性原理來實現的,具體是不是這樣還沒看。

四、解決方案

1.修改主要站台web.config目錄,讓運行時除了載入bin目錄中的檔案,還可以從其它目錄載入

 <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">  <probing privatePath="Plugins/temp/" /> </assemblyBinding> </runtime>

2.開發一個簡易的外掛程式管理類,這個類的作用就是在Application_Start之前就把Plugins各子目錄中的dll拷貝到第1步指定的檔案夾中,為了讓demo儘可能簡單,沒有對重複的dll進行檢測(比如外掛程式中引用了ef程式集,主要站台也引用了,在網站bin目錄中已經存在ef的dll了,就沒必要再把外掛程式中的dll拷貝到上面設定的動態程式集目錄中)

using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Reflection;using System.Text;using System.Threading.Tasks;using System.Web;using System.Web.Compilation;using System.Web.Hosting;[assembly: PreApplicationStartMethod(typeof(Plugins.Core.PreApplicationInit), "Initialize")]namespace Plugins.Core{ public class PreApplicationInit {  static PreApplicationInit()  {   PluginFolder = new DirectoryInfo(HostingEnvironment.MapPath("~/plugins"));   ShadowCopyFolder = new DirectoryInfo(HostingEnvironment.MapPath("~/plugins/temp"));  }  /// <summary>  /// 外掛程式所在目錄資訊  /// </summary>  private static readonly DirectoryInfo PluginFolder;  /// <summary>  /// 程式應行時指定的dll目錄  /// </summary>  private static readonly DirectoryInfo ShadowCopyFolder;  public static void Initialize()  {   Directory.CreateDirectory(ShadowCopyFolder.FullName);   //清空外掛程式dll運行目錄中的檔案   foreach (var f in ShadowCopyFolder.GetFiles("*.dll", SearchOption.AllDirectories))   {    f.Delete();   }   foreach (var plug in PluginFolder.GetFiles("*.dll", SearchOption.AllDirectories).Where(i=>i.Directory.Parent.Name== "plugins"))   {    File.Copy(plug.FullName, Path.Combine(ShadowCopyFolder.FullName, plug.Name), true);   }   foreach (var a in    ShadowCopyFolder    .GetFiles("*.dll", SearchOption.AllDirectories)    .Select(x => AssemblyName.GetAssemblyName(x.FullName))    .Select(x => Assembly.Load(x.FullName)))   {    BuildManager.AddReferencedAssembly(a);   }  } }}

3.如何讓View引擎找到我們的視圖呢?答案是重寫RazorViewEngine的方法,我採用了約定大於配置的方式(假設我們的外掛程式項目命名空間為Plugins.Apps.Sms,那麼預設的控制器命名空間為Plugins.Apps.Sms.Controllers,外掛程式產生後的檔案夾必須為/Plugins/Plugins.Apps.Sms/),通過分析當前控制器就可以知道當前外掛程式的View目錄位置

using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;using System.Web;using System.Web.Mvc;using System.Web.WebPages.Razor;namespace Plugins.Web{ public class CustomerViewEngine : RazorViewEngine {  /// <summary>  /// 定義視圖頁所在地址。  /// </summary>  private string[] _viewLocationFormats = new[]  {   "~/Views/Parts/{0}.cshtml",   "~/Plugins/{pluginFolder}/Views/{1}/{0}.cshtml",   "~/Plugins/{pluginFolder}/Views/Shared/{0}.cshtml",   "~/Views/{1}/{0}.cshtml",   "~/Views/Shared/{0}.cshtml",  };  public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)  {   string ns = controllerContext.Controller.GetType().Namespace;   string controller = controllerContext.Controller.GetType().Name.Replace("Controller", "");   //說明是外掛程式中的控制器,View目錄需要單獨處理   if (ns.ToLower().Contains("plugins"))   {    var pluginsFolder = ns.ToLower().Replace(".controllers", "");    ViewLocationFormats = ReplacePlaceholder(pluginsFolder);   }   return base.FindView(controllerContext, viewName, masterName, useCache);  }  /// <summary>  /// 替換pluginFolder預留位置  /// </summary>  /// <param name="folderName"></param>  private string[] ReplacePlaceholder(string folderName)  {   string[] tempArray = new string[_viewLocationFormats.Length];   if (_viewLocationFormats != null)   {    for (int i = 0; i < _viewLocationFormats.Length; i++)    {     tempArray[i] = _viewLocationFormats[i].Replace("{pluginFolder}", folderName);    }   }   return tempArray;  } }}

然後在主要站台的Global.asax中將Razor引擎指定為我們重寫過的

4.開始製作一個外掛程式目錄,跟我們平時建立的MVC項目並沒有太大區別,只是發布時需要做一些設定。

.產生路徑要按照第3條的約定來寫,不然會找不到視圖檔案

.View目錄下的web.config和.cshtml檔案要複製到組建目錄(在檔案中點右鍵)

3.設定引用項目中的產生屬性,主程式下面已經有了的就把“複製到輸出目錄”設定為無,要不然拷貝到動態bin目錄時會出錯,可以對第2步中的那個類改造一下,加入檔案比較功能,bin目錄中沒有的,才拷貝到動態bin目錄中。

4.產生後的目錄結構如下:

5.跑一下,一切正常,外掛程式中的控制器工作正常,視圖中引用了Model也沒問題

到此,一個外掛程式系統的核心部分就算完成了,你可繼續進行擴充,增加外掛程式的發現、安裝、卸載功能,這些相對於核心功能來說,都是小兒科。後續我會基於Abp架構出一個外掛程式系統的文章,有興趣的把小板凳準備好,瓜子花生買上:)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.