當系統越來越模組化,大量library開始透過nuget方式組成的時候,開始會遇到一個情況,那就是Dll版本問題(Dll Hell)。
如果對於Dll版本問題沒有一些了解的情況下,常常會遇到明明Dll存在bin下面,但是還是出現找不到的錯誤訊息:
這篇將對於.Net如何處理Dll Hell的問題做一些介紹。
解決方式 - 手動設定config - TL;DR
要解決這個問題基本上是透過在config設定bindingRedirect
,以上面為例子是找不到Newtonsoft.Json 6.0.0.0版本
,因此設定:
- 打開config - 如果是web就是
web.config
,如果是console就是app.config
- 加入以下在
configuration
->下runtime
面:<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="Newtonsoft.Json" culture="neutral" publicKeyToken="30ad4fe6b2a6aeed"/> <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0"/> </dependentAssembly> </assemblyBinding>
- 每一個dll版號就加入一個
dependentAssembly
assemblyIdentity
- 用來設定那個dll要對應。其他還蠻直覺,不過publicKeyToken
可以透過:- Google 搜索
- 用powershell - 例如 ([system.reflection.assembly]::loadfile("c:\Newtonsoft.Json.dll")).FullName 會取得
Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed
- 使用
sn
工具 - sn -T "c:\Newtonsoft.Json.dll" - 會取得Public key token is 30ad4fe6b2a6aeed
bindingRedirect
設定版號對應oldVersion
- 那些版號要對應 - 可以用-
來包含版號區間。newVersion
- 在oldVersion
符合的版號要對應到那個版號
注意,newVersion
不代表一定比oldVersion
高。換句話說也有可能高版本全部要用低版本的dll(不過這種情景比較少 ,因為一般都是高版本會向下兼容)注意版號取得的方式,用Powershell最準,如果用 檔案總管 點 右鍵 檢查的版號不一定準。
解決方式 - 自動設定 - TL;DR
Visual Studio (簡稱VS) 可以幫忙自動設定正確的binding,這邊又有分console和web差異。
Console自動設定binding
- 把console的csproj unload並且開啟修改畫面
- 加入
AutoGenerateBindingRedirects
到PropertyGroup
直接加入
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
- 重新rebuild專案
Web自動設定binding
和console不同,VS不會自動調整Web.config
,但是VS會有提示。
當build完之後,檢查Warning
頁簽
可以直接對warning點兩下,會自動問說要不要加入,如果要加入,會自動修改Web.confi。
問題發生的原因
我們都知道同一個版本的dll同一時間只能夠有一個版本。
假設專案A使用library C的1.0版本,然後專案B使用library C的2.0版本,當build的時候,坑定只會剩下一個library C。換句話說要嘛是1.0要嘛是2.0.
那麼問題來了,假設留下來的是1.0,而專案B需要的是library 2.0版本,.Net runtime怎麼run的起來,2.0不存在啊!因為這樣所以炸掉。
因此我們需要有個東西告訴runtime,到底要用什麼版本。
問題重現
要能夠了解問題和如何解決就要有一個能夠重現問題的專案,因此接下來要打造一個有問題的專案。會分廠兩個部分,console和web。
Console
console的部分大概是:
- 建立個console專案
- 使用
Newtonsoft.Json
7.0版本 - 建立一個library專案
- 在library專案使用
Newtonsoft.Json
6.0版本 - 並且建立一個方法 在
Class1
加入一個Convert
的方法:public string Convert() { return JsonConvert.SerializeObject(new { From = "Lib"}); }
- 在console使用library並且run起來
- console的main邏輯如下:
static void Main(string[] args) { Console.WriteLine(JsonConvert.SerializeObject(new { From = "Console"})); Console.WriteLine(new Class1().Convert()); Console.ReadLine(); }
- 拿掉
AutoGenerateBindingRedirects
檢查產出的
bin\DllHellProblem.exe.config
發現有自動產生bindingRedirect
還記得上面自動設定提到VS專案會自動加入的部分,因此我們直接修改
DllHellProblem.csproj
,然後把AutoGenerateBindingRedirects
拿掉。記得先clean在run,發現直接出錯:
發現bin下面的config,
bindingRedirect
沒有自己產生所以出錯。手動加入就對了。
console範例完成可以再githu看到:https://github.com/alantsai-samples/dotnet-dll-hell-problem/tree/sample/console
git標籤:sample/console
兩種方式取得:
git clone https://github.com/alantsai-samples/dotnet-dll-hell-problem.git
git checkout sample/console
- 從github release下載:下載鏈接
Web
- 加入一個Asp .net Mvc的專案
- 在
HomeController
使用class library並且把web.config的bindingRedirect拿掉 在
HomeController
的Index
加入new ClassLibrary1.Class1().Convert();
在
Web.config
先把Newtonsoft.json
的bindingRedirect
拿掉。- build專案並且檢查Error List
build專案,檢查
Error List
console範例完成可以再githu看到:https://github.com/alantsai-samples/dotnet-dll-hell-problem/tree/sample/web
git標籤:sample/web
兩種方式取得:
git clone https://github.com/alantsai-samples/dotnet-dll-hell-problem.git
git checkout sample/web
- 從github release下載:下載鏈接
結語
在Console其實非常方便,因為VS會自動幫忙處理bindingRedirect,但是web不會。
不過web會在Error List
裡面以warning的方式告訴,並且方便自動加入。
這個故事告訴我們,要好好看Warning資訊。
希望透過這篇對於如何處理不同library使用到相同但是版號不同的套件不會出現找不到dll的錯誤。
參考資料
- 官方介紹
- https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/redirect-assembly-versions
- 官方介紹自動binding Redirect
- https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/how-to-enable-and-disable-automatic-binding-redirection
- 黑大介紹用檔案總管檢查版號的小陷阱 - 建議還是用Powershell比較不會有問題
- http://blog.darkthread.net/post-2015-11-25-assemblyinformationversion.aspx
沒有留言 :
張貼留言