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

.NET 程序集隔離利器:AppDomain 動態加載與卸載 Assembly 完整實戰

     編輯:關於.NET

問題背景

主程序通過 Assembly.LoadFile 載入插件 DLL 僅讀取元數據(如 FullName),但 DLL 文件會被進程獨佔,導致無法重新編譯/更新插件。Assembly 無卸載方法,必須卸載整個 AppDomain。

解決架構:雙層加載器模式

主程序 (Default AppDomain)
    │
    ├── LocalLoader (代理)
    │       │
    │       └── 創建 New AppDomain
    │               │
    │               └── RemoteLoader (MarshalByRefObject)
    │                       │
    │                       └── Assembly.LoadFrom(插件路徑)
    │                       └── 暴露屬性/方法供主程序調用
    │
    └── 使用完畢 → AppDomain.Unload(New AppDomain) → 釋放文件鎖定

核心代碼實現

1. RemoteLoader (跨域實體)


public class RemoteLoader : MarshalByRefObject {
    private Assembly _assembly;
    public void LoadAssembly(string fullPath) {
        _assembly = Assembly.LoadFrom(fullPath);
    }
    public string FullName => _assembly?.FullName;
}

2. LocalLoader (本地協調器)


public class LocalLoader {
    private AppDomain _appDomain;
    private RemoteLoader _remoteLoader;

    public LocalLoader() {
        var setup = new AppDomainSetup {
            ApplicationName = "PluginDomain",
            ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
            ShadowCopyFiles = "true",           // 啟用影子複製,避免鎖定原文件
            ShadowCopyDirectories = AppDomain.CurrentDomain.BaseDirectory
        };
        _appDomain = AppDomain.CreateDomain("PluginDomain", null, setup);

        string assemblyName = Assembly.GetExecutingAssembly().GetName().FullName;
        _remoteLoader = (RemoteLoader)_appDomain.CreateInstanceAndUnwrap(
            assemblyName, typeof(RemoteLoader).FullName);
    }

    public void LoadAssembly(string path) => _remoteLoader.LoadAssembly(path);
    public string FullName => _remoteLoader.FullName;

    public void Unload() {
        AppDomain.Unload(_appDomain);
        _appDomain = null;
    }
}

3. 主程序調用


private void button1_Click(object sender, EventArgs e) {
    using (var loader = new LocalLoader()) {
        loader.LoadAssembly(@"C:\Plugins\MyPlugin.dll");
        label1.Text = loader.FullName;
        // loader.Unload() 在 Dispose 或這裡顯式調用
    }
}

關鍵技術點

  • MarshalByRefObject:讓 RemoteLoader 透明代理,主程序調用實際在新 AppDomain 執行。
  • ShadowCopyFiles:啟用後運行時複製程序集到緩存目錄,原文件不被鎖定,支持熱更新。
  • 無法跨域返回 Assembly 對象:只能返回可序列化數據(字符串、基礎類型、DataSet等)或定義接口組件分離部署。
  • AppDomain.Unload 是唯一釋放程序集的方式,會觸發該域內所有對象的 Finalize。

適用場景

  • 插件系統熱加載/熱更新
  • 運行不受信代碼(沙箱)
  • 長時間運行服務需動態更新組件
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved