一個.NET Core下的開源外掛框架

xfrog發表於2020-11-05

    外掛模式歷史悠久,各種中大型軟體基本上都會實現外掛機制,以此支援功能擴充套件,從開發部署層面,外掛機制也可實現功能解耦,對於並行開發、專案部署、功能定製等都有比較大的優勢。

    在.NET Core下,一般我們基於.NET Core擴充套件庫進行開發,通常使用依賴注入、配置、設定(Options)等機制,如果將外掛模式與依賴注入、配置、設定進行結合,將可以提供非常靈活的擴充套件機制。基於此,我們實現了一個開源的外掛框架,本文將進行簡單的介紹。

目錄

PluginFactory外掛庫

  • 專案地址:
  • Nuget包:
  • 主要功能:
    • 外掛的自動載入
    • 通過外掛的初始化介面可讓外掛控制主應用的依賴注入
    • 外掛的啟動及停止,此機制可與.NET Core的Hosting擴充套件結合,在宿主啟動時自動啟動外掛
    • 抽象分離,你可以通過實現Xfrogcn.PluginFactory.Abstractions中的相關介面來完全實現自己的外掛載入、啟動等機制
    • 與.NET Core配置、設定、宿主等完美融合
    • 支援外掛依賴程式集的多版本載入

主要概念

  • 外掛載入器:對應IPluginLoader介面,負責從指定位置載入外掛程式集
  • 外掛工廠:對應IPluginFactory介面,負責外掛的例項化及外掛的啟動和停止
  • 外掛:對應IPlugin介面,所有外掛需要實現此介面,實現外掛的啟動及停止機制
  • 可初始化外掛:對應ISupportInitPlugin介面,通過此介面可以在依賴注入Provider構建之前向容器注入自定義的服務
  • 可配置外掛:對應ISupportConfigPlugin介面,通過此介面可將外掛配置與.NET Core的配置(Configuration)及設定(Options)機制集合

使用嚮導

    示例專案可參考:Xfrogcn.PluginFactory.Example Gitee地址 Github地址

安裝

    在主程式專案中新增Xfrogcn.PluginFactory

```dotnet
dotnet add package Xfrogcn.PluginFactory
```

    在外掛專案中新增Xfrogcn.PluginFactory.Abstractions

```dotnet
dotnet add package Xfrogcn.PluginFactory.Abstractions
```

在主程式中啟用

    可通過以下兩種方式來啟用外掛庫,一是通過在Host層級的Use機制以及在依賴注入IServiceCollection層級的Add機制,以下分別說明:

通過IHostBuilder的UsePluginFactory方法啟用外掛庫

```c#
var builder = Host.CreateDefaultBuilder(args);
builder.UsePluginFactory();
```

    UsePluginFactory具有多個過載版本,詳細請檢視API文件
預設配置下,將使用程式執行目錄下的Plugins目錄作為外掛程式集目錄, 使用宿主配置檔案作為外掛配置檔案(通常為appsettings.json)
你也可以通過使用帶有 AssemblyIEnumerable<Assembly> 引數的版本直接傳入外掛所在的程式集

通過IServiceCollection的AddPluginFactory方法啟用外掛庫

```c#
var builder = Host.CreateDefaultBuilder(args)
    .ConfigureServices((hostContext, services) =>
    {
        services.AddPluginFactory();
    });
```

    AddPluginFactory具有多個過載版本,詳細請檢視API文件
預設配置下,將使用程式執行目錄下的Plugins目錄作為外掛程式集目錄

注意: AddPluginFactory方法不會使用預設的配置檔案作為外掛配置,你需要顯式地傳入IConfiguration, 如果是在 ASP.NET Core 環境中,你可以在Startup類中直接獲取到

編寫外掛

外掛是實現了IPlugin介面的類,在外掛庫中也提供了PluginBase基類,一般從此類繼承即可。標準外掛具有啟動和停止方法,通過IPluginFactory進行控制。

要編寫外掛,一般遵循以下步驟:

  1. 建立外掛專案(.NET Core 類庫),如TestPluginA

  2. 新增Xfrogcn.PluginFactory.Abstractions

     ```nuget
     dotnet add package Xfrogcn.PluginFactory.Abstractions
     ```
    
  3. 建立外掛類,如Plugin,從PluginBase繼承

     ```c#
     public class Plugin : PluginBase
     {
         public override Task StartAsync(IPluginContext context)
         {
             Console.WriteLine("外掛A已啟動");
             return base.StartAsync(context);
         }
    
         public override Task StopAsync(IPluginContext context)
         {
             Console.WriteLine("外掛A已停止");
             return base.StopAsync(context);
         }
     }
     ```
    

    啟動或停止方法中可通過context中的ServiceProvider獲取注入服務

  4. 通過PluginAttribute特性設定外掛的後設資料

     ```c#
     [Plugin(Alias = "PluginA", Description = "測試外掛")]
     public class Plugin : PluginBase
     {
     }
     ```
    

    外掛後設資料以及外掛載入的外掛列表資訊可以通過IPluginLoader.PluginList獲取

外掛啟動

    IPluginFactory本身實現了.NET Core擴充套件庫的IHostedService機制,故如果你是在宿主環境下使用,如(ASP.NET Core),外掛的啟動及停止將自動跟隨宿主進行
如果未使用宿主,可通過獲取IPluginFactory例項呼叫相應方法來完成

```c#
// 手動啟動
var pluginFactory = provider.GetRequiredService<IPluginFactory>();
await pluginFactory.StartAsync(default);
await pluginFactory.StopAsync(default);
```

編寫支援初始化的外掛

    在很多場景,我們需要在外掛中控制宿主的依賴注入,如注入新的服務等,這時候我們可通過實現支援初始化的外掛(ISupportInitPlugin)來實現,該介面的Init方法將在依賴注入構建之前呼叫,通過方法引數IPluginInitContext中的ServiceCollection可以控制宿主注入容器。

```c#
[Plugin(Alias = "PluginA", Description = "測試外掛")]
public class Plugin : PluginBase, ISupportInitPlugin
{
    public void Init(IPluginInitContext context)
    {
        // 注入服務
        //context.ServiceCollection.TryAddScoped<ICustomerService>();
    }
}
```

使用外掛配置

    外掛支援 .NET Core 擴充套件庫中的Options及Configuration機制,你只需要從SupportConfigPluginBase<TOptions>類繼承實現外掛即可,其中TOptions泛型為外掛的配置型別。外掛配置自動從宿主配置或啟用外掛工廠時傳入的配置中獲取,外掛配置位於配置下的Plugins節點,該節點下以外掛類名稱或外掛別名(通過PluginAttribute特性指定)作為鍵名,此鍵之下為外掛的配置,如以下配置檔案:

```appsettings.json
{
    "Plugins": {
        "PluginA": {
            "TestConfig": "Hello World"
        },

    }
}
```

擴充套件PluginA實現配置:

  1. 定義配置類,如PluginOptions

     ```c#
     public class PluginOptions
     {
         public string TestConfig { get; set; }
     }
     ```
    
  2. 實現外掛

     ```c#
     [Plugin(Alias = "PluginA", Description = "測試外掛")]
     public class Plugin : SupportConfigPluginBase<PluginOptions>, ISupportInitPlugin
     {
    
         public Plugin(IOptionsMonitor<PluginOptions> options) : base(options)
         {
         }
    
         public void Init(IPluginInitContext context)
         {
             // 注入服務
             //context.ServiceCollection.TryAddScoped<ICustomerService>();
             Console.WriteLine($"Init 外掛配置:{Options.TestConfig}");
         }
    
         public override Task StartAsync(IPluginContext context)
         {
             Console.WriteLine("外掛A已啟動");
             Console.WriteLine($"StartAsync 外掛配置:{Options.TestConfig}");
             return base.StartAsync(context);
         }
    
         public override Task StopAsync(IPluginContext context)
         {
             Console.WriteLine("外掛A已停止");
             return base.StopAsync(context);
         }
     ```
    

    注意:在外掛初始化方法中也可使用注入的配置

  3. 跨外掛配置

    有些配置可能需要在多個外掛中共享,此時你可通過Plugins下的_Share節點進行配置,此節點下配置將會被合併到外掛配置中,可通過PluginOptions進行訪問。

     ```appsettings.json
     {
         "Plugins": {
             "PluginA": {
             },
             "_Share": {
                 "TestConfig": "Hello World"
             }
         }
     }
     ```
    

外掛化 ASP.NET Core

    要讓 ASP.NET Core 獲取得到外掛中的控制器,你只需要在外掛的初始化方法Init中,向MVC注入外掛程式集:

```c#
context.ServiceCollection.AddMvcCore()
    .AddApplicationPart(typeof(Plugin).Assembly);
```

相關文章