WebApi 插件式構建方案:發現並加載程序集


插件式的 WebApi 開發,首要面對的問題就是程序集的發現。因為開發的過程中,都是在各自的解決方案下進行開發,部署后是分模塊放在一個整體的的運行時網站下。

約定

這里我根據上一節的設定,把插件打包完成后的文件夾,放入網站 bin 目錄下。重復一下這樣做的好處:在插件的配置或者程序集發生變動后,網站會直接重新啟動。

這是 IIS 的機制,和 WebApi 無關。

  • 約定插件的文件夾名稱使用 00_Name 的形式,可以更方便的按照我們的要求排列插件。
  • 約定插件的配置文件為插件根目錄 PluginConfig.xml 文件
  • 約定插件的配置文件如下(后續隨着功能的添加會適當添加內容)
<?xml version="1.0" encoding="UTF-8"?>
  <configuration enabled="true">
    <description>授權支持插件</description>
    <assemblies>
      <add type="relative">bin/Intime.AuthorizationService.dll</add>
      <add type="relative">bin/Intime.AuthorizationService.Services.dll</add>
      <add type="relative">bin/Intime.AuthorizationService.Data.dll</add>
      <add type="relative">bin/Intime.AuthorizationService.Data.Repository.dll</add>
    </assembiles>
</configuration>

解釋一下 XML 配置文檔的含義:

  • enabled="true" 表示當前插件的可用性,只有值為 true 的模塊才會進行后續操作;否則不做任何操作。
  • description 子元素表示當前模塊的自然語義名稱,在程序上沒有任何含義,面向自然人說明當前模塊的作用。
  • assemblies 子元素表示當前模塊下需要加載的程序集列表。
    • add 元素表示添加一個程序集文件:目前有且只有一個 add 元素受支持。
    • type 表示內含程序集路徑的類型:
      • relative 表示相對路徑,為程序集文件相對於當前配置文件的路徑:比如第一個文件就是在當前目錄的子目錄 bin 目錄下。
      • absolute 表示絕對路徑,表示程序不必做額外工作就可以根據路徑信息找到文件:此處沒有栗子
    • 內含文本表示程序集的路徑信息。

BuildManager 程序集加載規則

在實現程序集加載之前,我們有必要大概了解一下 BuildManager 類加載程序集的規則。

首先,是項目引用,被網站項目引用的 GAC 程序集,都會列入到引用列表中;其次,就是這個類很強大,只要你將程序集放入網站,它就能知道網站運行需要加載這個程序集;最后,還有個不常用的設置,Web.config 文件的 runtime 配置節點中引進的目錄,也會被加載。所以我們要加載的程序集,必須是這三個地方所沒有的程序集。

另外,就是程序集多版本的問題。這里我們約定更新的版本完全兼容老版本,否則就需要升級代碼以適配這個功能。在網站運行出現多個版本存在的情況下,我們約定如下原則:

  • bin 目錄是最先加載的路徑,后續插件加載的程序集版本必須小於等於 bin 目錄下程序集的版本。
  • 非 .Net 框架庫自帶的程序集,一律要拷貝到網站 bin 目錄下:請使用 NuGet 管理第三方程序集。
  • 不要使用 Web.config 文件的 runtime 配置節點加載個性目錄。

這樣,我們可以定義包含這些元數據的類:配置文件信息、加載的程序集列表,在 PreApplicationStartMethodAttribute 程序集特性設定的方法內,將我們的程序集加到 BuildManager 管理的 程序集列表中。在程序運行時,.Net 就可以找到我們的這些程序集了。

部分源代碼:自然語義

代碼是在 Mac 下用文本編輯器寫出來的,請自行腦補。

public class DynamicModule
{
  public static DynamicModule Instance
  {
    get{ return Instance == null ? (Instance = new DynamicModule()) : Instance; }
  }
  
  public string BaseDirectory { get; set; }
  
  public ModuleMetadata[] BaseDirectory { get; set; }
  
  ctor()
  {
    BaseDirectory = Path.Combine(App.BaseDirectory, "bin");
    
    var modules = Directory.GetFiles(BaseDirectory, "PlugConfig.xml", AllDirectory)
                           .Where(p => p.Configuration.Enabled);
                           
    bar baseAssemblies = AssemblyName.GetAssemblyNames(BaseDirectory, "*.dll", TopDirectory);
    
    var data = baseAssemblies.Intersect(modules.LoadedAssemblyNames)
                             .Where(p => p.CodeBase != BaseDirectory);
    if(data.Any())
      throw new ModuleConfiguration(string.Format("程序集 {0} 裝載出現異常!", string.Join(data)));
      
    modules.Foreach(p => p.LoadAssemblies());
  }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM