《西游記》中真假美猴王讓人着實難以區分,但是我們熟知了其中的細節也不難把他們剝去表象分別出來。對問題不太關心的可以直接調到文中關於.Net文件版本的介紹
問題
最近在編譯AKKA.net 時出現了一個問題:Newtonsoft.Json.dll 沖突.
C:\Program Files (x86)\MSBuild\14.0\bin\Microsoft.Common.CurrentVersion.targets(1819,5): warning MSB3243: No way to resolve conflict between "Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed" and "Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed". Choosing "Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed" arbitrarily.
Consider app.config remapping of assembly "Newtonsoft.Json, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed" from Version "4.5.0.0" [C:\Program Files (x86)\Microsoft Visual Studio 12.0\Blend\Newtonsoft.Json.dll] to Version "7.0.0.0" [D:\TestProjects\GitHub\akka.net\src\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll] to solve conflict and get rid of warning.
解決
分析問題
在VisualStudio給出的錯誤提示中,第一句話着實讓人摸不着頭腦。還好第二句給出沖突Dll文件的具體路徑。但是奇怪的的是明明通過以下命令:
獲取的Newtonsoft.Json.dll 應該是 “7.0.1”版本,怎么出來讓remaping 到 “7.0.0”版本。
奇怪啊!!
核實Nuget到Newtonsoft.Json.dll
打開Dll文件核實下:
雙擊“1 [Neutral]”圖標,將會打開dll程序集的詳細信息如下:
原來Assembly的確是“7.0.0.0”
那好吧,我們再來看下Blend 下的同名文件
Blend文件夾下的Newtonsoft.Json.dll
Assembly Version 4.5.0.0 這和上面7.0.0.0完全不一樣啊,VisualStudio(VS)怎么會把他們混淆了??
嘗試解決
讓我們來猜下原因吧,那是不是VS找不到具體的7.0.0 的Dll呢,然后就找到了4.5.0 的DLL,結果就…??
打開報錯的項目,先卸載項目,然后才可以編輯查看項目文件:
打開項目
發現有兩個地方出現了Newtonsoft.Json :
在查閱過<choose>這個不常用節點的用法后,我猜這個是因為這個節點導致,於是直接Ctrl+H 替換成於Reference相同的路徑,編譯,成功通過!
問題相關知識
上一堆截圖,到底要干嘛呢?相信一定有人會疑惑。
對,上面折騰半天的目的就是為了找出以下幾個關於.Net程序集版本至關重要的三大版本,第一次接觸一定很好奇(也許是郁悶,因為出問題鬧心啊)。
三個重要的.Net 程序集版本
在.Net中有三個重要的版本信息,因為經常混淆,所以要大家注意:
// Assembly mscorlib, Version 2.0.0.0 [assembly: AssemblyFileVersion("2.0.50727.3521")] [assembly: AssemblyInformationalVersion("2.0.50727.3521")] [assembly: AssemblyVersion("2.0.0.0")]
版本信息的四部分,我們約定為Major Version(主版本), Minor Version(次版本), Build(編譯版本), and Revision(修訂版本)
AssemblyFileVersion
通常我們會手動設置 AssemblyFileVersion 中的Major和Minor 去體現程序集的版本,Build 和(或者) Revision 這兩個一般是由Build工具在每次編譯程序集時自動增加的。 我們可以用AssemblyFileVersion來作為程序集的唯一標識。(調試的時候我們就可以根據這個版本號找到相應dll,)
在項目開發中一般我們會用ChangeList(變更集)號去生成AssemblyFileVersion 的Build和Revision兩部分。這樣就很方便從dll找到相應的源代碼變更集。省去了自己單獨去記錄發布的dll和源代碼對應關系的繁瑣事項。
AssemblyFileVersion 存儲在Win32的版本資源中,所以可以通過資源瀏覽器(右擊屬性)查看程序集的相應AssemblyFileVersion 。
The CLR does not care about nor examine the AssemblyFileVersion.
AssemblyInformationalVersion
AssemblyInformationalVersion 目的是用於整個產品(某個dll或者exe)能夠有一個一致的(coherent)版本。這個產品可能含有很多個程序集,而且這些程序集可能有不同的版本標識策略或者根本不是同一個團隊開發的。
“For example, version 2.0 of a product might contain several assemblies; one of these assemblies is marked as version 1.0 since it’s a new assembly that didn’t ship in version 1.0 of the same product. Typically, you set the major and minor parts of this version number to represent the public version of your product. Then you increment the build and revision parts each time you package a complete product with all its assemblies.”
— Jeffrey Richter, CLR via C# (Second Edition) p. 57
The CLR does not care about nor examine the AssemblyInformationalVersion.
AssemblyVersion
AssemblyVersion 存儲在AssemblyDef的元數據列表清單,任何引用該AssemblyVersion 版本的dll(.exe)。
The AssemblyVersion is used by the CLR to bind to strongly named assemblies.(CLR唯一關心的版本)這一點至關重要。
只有AssemblyVersion版本完全一致的(匹配的)強命名程序集,才能編譯成功。例如,如果你引用了一個1.0.0.0的強命名A.dll,編譯后,你將A.DLL升級到了1.0.0.1.那么,不好意思,你的程序將會失敗,那就沒有辦法了嗎?有可以參考Assembly Binding Redirection 來做DLL重定向。
Assembly Version : This is the version number used by framework during build and at runtime to locate, link and load the assemblies. When you add reference to any assembly in your project, it is this version number which gets embedded. At runtime, CLR looks for assembly with this version number to load. But remember this version is used along with name, public key token and culture information only if the assemblies are strong-named signed. If assemblies are not strong-named signed, only file names are used for loading.
小心修改AssemblyVersion
當別的開發人員正在引用你發布的程序集是,你應該非常小心的去修改這些程序集的AssemblyVersion。AssemblyVersion的任何改動都意味着開發者不得不重新編譯應用程序(或者相應的dll)而不是簡單的直接文件覆蓋(新版本dll直接覆蓋就版本dll)。
- 如果要保證當前發布的dll向后兼容,請不要修改AssemblyVersion。
- 如果你有突破性的修改(脫胎換骨的大改動),那請修改AssemblyVersion。
補充,Redirect Binding的問題(程序集沖突解決方法,不含修改文件路徑的方法)
自動重定向
Starting with Visual Studio 2013, when you compile apps that target the .NET Framework 4.5.1, binding redirects may be automatically added to the app configuration file to override assembly unification. Binding redirects are added if your app or its components reference more than one version of the same assembly, even if you manually specify binding redirects in the configuration file for your app. The automatic binding redirection feature affects traditional desktop apps and web apps that target the .NET Framework 4.5.1, although the behavior is slightly different for a web app. You can enable automatic binding redirection if you have existing apps that target previous versions of the .NET Framework, or you can disable this feature if you want to keep manually authored binding redirects.
在項目文件(.csproj or .vbproj)的<PropertyGroup>標簽下添加<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>可以讓VisualStudio自動重定向到相應的DLL。
示例:
<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <ProjectGuid>{123334}</ProjectGuid> ... <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> </PropertyGroup> ... </Project>
Remaping來重定向
<runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed"/> <bindingRedirect oldVersion="0.0.0.0-4.5.0.0" newVersion="6.0.0.0"/> </dependentAssembly> </assemblyBinding> </runtime>
注意:
- <bindingRedirect>標簽中屬性
newVersion 和
oldVersion 都是AssemblyVersion。
在應用程序配置文件(web.config或者App.config)中<runtime>
標簽是在<configuration></configuration>
標簽內.xmlns
屬性表示相應 XML namespace.xmlns="schemas-microsoft-com:asm.v1"
是程序集開發人員避免標簽沖突而添加的。oldVersion
中可以包含所有的兼容的dll版本publicKeyToken 是該程序集公鑰
. 通過sn -T assembly_file_name命令可以獲取到也可同Reflector查看.
總結
有的同仁總覺得.Net簡單,GC幫我做了好多內存管理工作,我們就可以隨心所欲,為所欲為。且不知任何技術都有他不可告人的軟肋,廣告總是大篇幅的說着自己的完美哪有功夫給大家展示自己的不足(當然這里很多時候涉及到了設計准則哲學,如框架設計會優先考慮20%的經常使用功能,而暫緩考慮其他的80%不常用的功能)。
所以學習一門技術一定要知道他的底層原理和關鍵點。知道的越多,你被坑的概率就會越少,效率就越高。
參考
Newtonsoft.Json Assembly Conflict
《CLR Via C#》