首頁  >  文章  >  後端開發  >  使用ASP.NET中MVC引擎開發插件系統的範例詳解

使用ASP.NET中MVC引擎開發插件系統的範例詳解

黄舟
黄舟原創
2017-05-21 11:23:532195瀏覽

這篇文章主要為大家詳細介紹了使用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框架出一個插件系統的文章,有興趣的把小板凳準備好,瓜子花生買上:)

以上是使用ASP.NET中MVC引擎開發插件系統的範例詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn