Natasha 4.0 探索之路系列(二) 「域」与插件
域與ALC
在 Natasha 發(fā)布之后有不少小伙伴跑過來問域相關(guān)的問題,能不能兼容 AppDomain、如何使用 AppDomain、為什么 CoreAPI 閹割了 AppDomain 等一系列的問題。
今天答復(fù)一下:
首先 AppDomain 作為程序集隔離容器的存在,是風(fēng)靡了 .NET Framework 的各大版本,被譽(yù)為是輕量級(jí)進(jìn)程,由 AppDomain 發(fā)展的特性和操作也很多。而 Natasha 采用的是 AssemblyLoadContext 簡稱 "ALC";ALC 是 .NET Core 版本后出現(xiàn)的操作類,這個(gè)類在 .NET Core 及以后的版本中,只要加載依賴項(xiàng),就會(huì)調(diào)用它。有趣的是,你在調(diào)試代碼過程中如果去觀察它,可以看到它緩存程序集的數(shù)量在增加。因?yàn)檫€沒運(yùn)行到的程序集可以先不加載,檢測代碼 AssemblyLoadContext.Default.Assemblies.Count();
其次它本不是域,或者不能稱為域。它和域的區(qū)別是,FW 支持多域,而 CORE 僅支持單域,CORE 就一個(gè)默認(rèn)域。ALC 的名字翻譯過來是,程序集加載上下文,看英文名字也是和域區(qū)分開了;
最后一點(diǎn)區(qū)別是域的卸載是強(qiáng)制的,ALC 的卸載是“協(xié)商”的,相比域而言,ALC 中的程序集所包含的元數(shù)據(jù)被保持引用,就不能被卸載,比如你反射出來的類或者方法或者其他什么的放到了一個(gè)主域的字典中,那么字典不毀,這個(gè) ALC 就沒辦法卸載,盡管 ALC 有 Unload 方法,卸載還是要看元數(shù)據(jù)是否被保持引用;
Natasha 設(shè)計(jì)初衷是使用隔離性較強(qiáng)的字眼,用域的概念來減少 .NET Core 帶來的新的理解成本,另外之前有打算兼容 AppDomain 的想法。
這個(gè)想法的優(yōu)先級(jí)不高:
是 .NET Core 是在 3.0 時(shí)出現(xiàn)比較明顯的分水嶺,包括依賴解析,上下文域識(shí)別等重要特性的支持;
是 Roslyn 對(duì) FW 的支持不能低于(4.6.1);
是 UT測試需要區(qū)分版本來做,很麻煩,插件部分的測試不簡單;
是 個(gè)人精力原因,還要工作,還要維護(hù)其他項(xiàng)目;
這里也希望公司們都能平穩(wěn)度過升級(jí)期,早點(diǎn)迎接更好更實(shí)用的"未來技術(shù)"。
Natasha 域的使用
插件的開發(fā)技巧
這里不得不回顧一下插件開發(fā)的知識(shí),它可不是像培訓(xùn)機(jī)構(gòu)講的編譯一個(gè) DLL 然后 Assembly.LoadFrom 就可以的。首先要了解加載插件的兩個(gè)側(cè)重點(diǎn),插件依賴打包和插件依賴管理。
插件依賴打包:首先插件生成時(shí),你需要把必要的引用庫一起打包,此時(shí)需要在工程文件的 PropertyGroup 節(jié)點(diǎn)中添加 <EnableDynamicLoading>true</EnableDynamicLoading> 讓編譯程序輸出依賴文件,同時(shí)不要忘了交付 "xxx.deps.json",這是讓宿主程序解析依賴的關(guān)鍵;
插件依賴管理:如果你的接口 IPlugin 給到插件開發(fā)人員,讓他按照這個(gè)接口去寫功能,那么當(dāng)他交付插件時(shí),你不能再將他包里的 IPlugin 再引進(jìn)來。否則如下代碼將報(bào)錯(cuò),(var plugin = (IPlugin)Activtor.Create(pluginA);) 類型轉(zhuǎn)換錯(cuò)誤,原因是代碼中的 IPlugin 在主域中使用,而 pluginA 是加載到其他域中的,而且在那個(gè)域里也存在一個(gè) IPlugin,這個(gè)接口類型不同于主域的接口類型,因此在轉(zhuǎn)換時(shí)會(huì)引發(fā)類型轉(zhuǎn)換的錯(cuò)誤。
解決方法1:讓插件開發(fā)人員在自己的工程添加設(shè)置,自動(dòng)排除這個(gè)主要依賴(官方的推薦做法):
解決方法2:在實(shí)現(xiàn)的 ALC 中添加過濾方法排除 IPlugin
以上是基本的插件開發(fā)知識(shí),如果你還不了解,可以讀一讀微軟插件開發(fā)文檔。
https://docs.microsoft.com/zh-cn/dotnet/core/tutorials/creating-app-with-plugin-support
單獨(dú)使用 NatashaDomain :
引入包 DotNetCore.Natasha.Domain 包。
加載插件
避坑指南
如果您使用以上 API 將插件加載到同一個(gè)域,會(huì)出現(xiàn)很多問題:
建議:
寫插件時(shí),本身解決好引用管理問題;
如果插件過于龐大,請(qǐng)將插件功能解耦,并加載到不同域中反射給主域執(zhí)行;
主域要對(duì)依賴使用版本檢查,請(qǐng)?jiān)诓寮虞d代碼之前執(zhí)行一些功能。比如 _ = typeof(Dapper.CommandDefinition); 盡管這句沒有用,但它將迫使運(yùn)行時(shí)將 Dapper 的程序集加載到默認(rèn)上下文的緩存中,這樣在你加載插件時(shí),如果遇到 Dapper 依賴,將觸發(fā)版本檢查詳見代碼。https://github.com/dotnetcore/Natasha/blob/main/samples/PluginSample/PluginSample/Program.cs#L145
結(jié)尾
您可以自行查看案例代碼。NatashaDomain 是 Natasha 動(dòng)態(tài)編譯的父級(jí),Natasha 動(dòng)態(tài)編譯中的 NatashaReferenceDomain 繼承了此類,因此如果您想使用 Natasha 進(jìn)行動(dòng)態(tài)構(gòu)建請(qǐng)使用 NatashaReferenceDomain。下一篇將講解 Natasha 的基本編譯知識(shí)。
總結(jié)
以上是生活随笔為你收集整理的Natasha 4.0 探索之路系列(二) 「域」与插件的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Natasha 4.0 探索之路系列(一
- 下一篇: Blog.Core高级进阶:共赴五年之约