首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

.NET 插件架構實作指南:介面契約、動態載入與版本隔離

     編輯:關於.NET

插件機制讓系統在不重新編譯主程式的前提下,透過放置 DLL 即可擴充功能。本文示範基於介面契約的標準插件架構,涵蓋介面定義、插件實作、主程式動態發現與載入全流程。

架構核心:共用介面組件

建立獨立專案 PluginInterface,僅定義介面,編譯為 DLL 供主程式與插件共同參考:

// PluginInterface.dll
public interface IPlugin
{
    int MovementID { get; set; }
    string MovementName { get; set; }
    int RetryMinute { get; set; }
    string ConnectionString { get; set; }
    event Action<string> RaiseLog;
    void Initialize();
}

關鍵原則:主程式與插件必須參考同一個介面 DLL 檔案,否則即使介面內容相同,型別身分不同會導致 InvalidCastException。

插件實作專案

新建 Class Library 專案,參考 PluginInterface.dll,實作介面:

// MyPlugin.dll
using PluginInterface;

public class DataSyncPlugin : IPlugin
{
    public int MovementID { get; set; } = 1001;
    public string MovementName { get; set; } = "資料同步作業";
    public int RetryMinute { get; set; } = 30;
    public string ConnectionString { get; set; }
    public event Action<string> RaiseLog;

    public void Initialize()
    {
        // 實際業務邏輯:資料抓取、轉換、寫入
        RaiseLog?.Invoke($"[{MovementName}] 開始執行...");
        // ...
        RaiseLog?.Invoke($"[{MovementName}] 執行完成!");
    }

    // 內部私有方法,不暴露於介面
    private void SaveLog(DateTime dt, string msg) { /* ... */ }
}

編譯輸出 MyPlugin.dll 置於主程式 Plugins 目錄。

主程式動態載入引擎

using System;
using System.IO;
using System.Reflection;
using PluginInterface;

public class PluginManager
{
    private readonly string _pluginDirectory;
    public event Action<string> OnPluginLog;

    public PluginManager(string pluginDir = "Plugins")
    {
        _pluginDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, pluginDir);
        if (!Directory.Exists(_pluginDirectory))
            Directory.CreateDirectory(_pluginDirectory);
    }

    public IPlugin[] LoadAllPlugins()
    {
        var plugins = new List<IPlugin>();
        var dllFiles = Directory.GetFiles(_pluginDirectory, "*.dll");

        foreach (var file in dllFiles)
        {
            try
            {
                Assembly asm = Assembly.LoadFrom(file);
                foreach (Type type in asm.GetExportedTypes())
                {
                    if (typeof(IPlugin).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract)
                    {
                        IPlugin plugin = (IPlugin)Activator.CreateInstance(type);
                        plugin.RaiseLog += msg => OnPluginLog?.Invoke($"[{plugin.MovementName}] {msg}");
                        plugins.Add(plugin);
                    }
                }
            }
            catch (Exception ex)
            {
                OnPluginLog?.Invoke($"[載入失敗] {file}: {ex.Message}");
            }
        }
        return plugins.ToArray();
    }
}

使用範例

var manager = new PluginManager();
manager.OnPluginLog += msg => Console.WriteLine($"[LOG] {msg}");

var plugins = manager.LoadAllPlugins();
foreach (var p in plugins)
{
    Console.WriteLine($"發現插件:{p.MovementName} (ID:{p.MovementID})");
    p.ConnectionString = ConfigurationManager.ConnectionStrings["MainDB"].ConnectionString;
    p.Initialize();  // 觸發插件邏輯
}

進階議題與最佳實踐

1. 版本隔離與相依性管理

  • 插件若參考不同版本的共用庫(如 Newtonsoft.Json),易發生 FileLoadException。
  • 方案:AppDomain 隔離(.NET Framework)或 AssemblyLoadContext(.NET Core 3.0+),各插件擁有獨立載入上下文。

2. 插件熱更新

  • FileSystemWatcher 監控目錄變更,觸發重新載入。
  • 需協調執行中插件的優雅關閉(實作 IAsyncDisposable 或 IDisposable)。

3. 安全性考量

  • 僅載入簽名驗證通過的插件(強名稱簽章 / Authenticode)。
  • 限制插件權限(沙箱),防止惡意代碼存取檔案系統、網路。

4. 生命週期管理

public interface IPlugin : IDisposable
{
    // ... 既有成員 ...
    Task StartAsync(CancellationToken ct);
    Task StopAsync(CancellationToken ct);
}

主程式負責協調 StartAsync/StopAsync,確保資源正確獲取與釋放。

目錄結構範例

MyApp/
├── MyApp.exe
├── PluginInterface.dll
├── Plugins/
│   ├── DataSyncPlugin.dll
│   ├── ReportGenerator.dll
│   └── NotificationSender.dll
└── Config/
    └── plugins.json  (可選:啟用/停用、參數配置)

總結

基於介面的插件架構三要素:
1. 契約優先:共用介面 DLL 定義邊界。
2. 約定優於配置:固定目錄、掃描 *.dll、實作介面即可載入。
3. 隔離與治理:版本隔離、安全簽名、生命週期管理。
掌握此模式,即可打造高擴充性、低耦合的企業級應用系統。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved