使用 .NET Core 3.0 的 AssemblyLoadContext 實現插件熱加載
一般情況下,一個 .NET 程序集加載到程序中以后,它的類型信息以及原生代碼等數據會一直保留在內存中,.NET 運行時無法回收它們,如果我們要實現插件熱加載 (例如 Razor 或 Aspx 模版的熱更新) 則會造成內存泄漏。在以往,我們可以使用 .NET Framework 的 AppDomain 機制,或者使用解釋器 (有一定的性能損失),或者在編譯一定次數以后重啟程序 (Asp.NET 的 numRecompilesBeforeAppRestart) 來避免內存泄漏。
因為 .NET Core 不像 .NET Framework 一樣支持動態創建與卸載 AppDomain,所以一直都沒有好的方法實現插件熱加載,好消息是,.NET Core 從 3.0 開始支持了可回收程序集 (Collectible Assembly),我們可以創建一個可回收的 AssemblyLoadContext,用它來加載與卸載程序集。
AssemblyLoadContext存在主要用於提供程序集加載隔離,不提供Appdomain中的安全邊界(隔離),.net core安全邊界(隔離)由進程負責。 它允許在單個進程中加載同一程序集的多個版本。 它將替換 .NET Framework 中的多個實例提供的隔離機制 AppDomain 。
.net core 程序 默認的采用AssemblyLoadContext.Default 加載程序依賴項。
因此我們可以通過AssemblyLoadContext.Default.Assemblies。來獲取當前程序集的所有的依賴項。
AppDomain方法:
- 獲取所有程序集
過時:var assembliesInAppDomain = AppDomain.CurrentDomain.GetAssemblies(); 新:var assembliesInAssemblyLoadContext = AssemblyLoadContext.Default.Assemblies;
- 加載一個程序集
過時:AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName("path")); 新:AssemblyLoadContext.Default.LoadFromAssemblyName(AssemblyName.GetAssemblyName("path"));
- 加載一個程序集 路徑或者字節數組:
過時:AppDomain.CurrentDomain.Load(File.ReadAllBytes("path")); 新:AssemblyLoadContext.Default.LoadFromStream(File.OpenRead("path")); // or AssemblyLoadContext.Default.LoadFromAssemblyPath("path");
新建一個Context並且把程序集加載進 這個Context中
AssemblyLoadContext pluginAssemblyLoadContext = new("pluginAssemblyLoadContext", true);//輸入Context名稱,指定是否可以卸載 Assembly assembly = pluginAssemblyLoadContext.LoadFromAssemblyPath(item);