我有两个程序集:HelloWorld.exe和Hello.dll。exe是主程序集,dll正在被主程序集使用。
我编译了HelloWorld.exe(1.0.0)和Hello.dll(1.0.0)。我把程序集放在另一个文件夹。
然后我将Hello.dll的版本更改为2.0.0,并继续用2.0.0版本覆盖Hello.dll 1.0.0。然后我启动HelloWorld.exe,它工作正常。
我预计它会立即崩溃和刻录,因为当我编译EXE时引用的Hello.dll是1.0.0。现在,1.0.0 DLL已被2.0.0取代,但它仍然工作!
根据MSDN:
默认情况下,程序集将只使用完全相同的类型用于构建和测试程序集的程序集(名称和版本号)。也就是说,如果您的程序集使用版本1.0.0.2中的类型对于另一个程序集,它将(默认情况下)不使用来自的相同类型另一个程序集的1.0.0.4版本。这种同时使用名字和版本来识别引用的程序集有助于避免"DLL地狱"升级到一个应用程序会影响其他应用程序的问题。
问题:
- 为什么它有效?
- 如何使其不工作? 附加问题:在构建过程中会发生什么?外部依赖的版本不是硬编码到主依赖吗?
注意Hello.dll不是强命名的
下面是HelloWorld.exe的清单:
// Metadata version: v2.0.50727
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .zV.4..
.ver 2:0:0:0
}
.assembly extern Hello
{
.ver 2:0:0:0
}
.assembly HelloWorld
{
...//snipped
}
这是从Fuslogvw.exe(程序集绑定日志查看器)获取的程序集绑定日志:
=== Pre-bind state information ===
LOG: User = ian.uy
LOG: DisplayName = Hello, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null
(Fully-specified)
LOG: Appbase = file:///C:/Users/ian.uy/Desktop/HelloWorld/HelloWorld/bin/Debug/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = NULL
Calling assembly : HelloWorld, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.
===
LOG: This bind starts in default load context.
LOG: No application configuration file found.
LOG: Using machine configuration file from C:WindowsMicrosoft.NETFrameworkv2.0.50727configmachine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///C:/Users/ian.uy/Desktop/HelloWorld/HelloWorld/bin/Debug/Hello.DLL.
LOG: Assembly download was successful. Attempting setup of file: C:Usersian.uyDesktopHelloWorldHelloWorldbinDebugHello.dll
LOG: Entering run-from-source setup phase.
LOG: Assembly Name is: Hello, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
LOG: Binding succeeds. Returns assembly from C:Usersian.uyDesktopHelloWorldHelloWorldbinDebugHello.dll.
LOG: Assembly is loaded in default load context.
我遇到过同样的事情,并试图查看msbuild日志:
msbuild /v:detailed /t:build
下面几行看起来很有趣:
统一依赖"Newtonsoft。Json,版本=8.0.0.0,文化=中性,都30 ad4fe6b2a6aeed"。在"C:srcBindingTestLib1binDebugLib1.dll"中使用此版本代替原始版本"7.0.0.0",因为AutoUnify是"真正的"。
另外,如果你在构建后检查生成的app.config文件,你可能会在那里看到app.config最初没有的自动绑定重定向。
所以我们观察到的行为与"自动组装统一"one_answers"自动绑定重定向"msbuild过程有关。
以下是文档中关于AutoUnify
参数的说明:
当为true时,生成的依赖关系图被自动视为如果有app的话。传入到AppConfigFile的配置文件参数。这个虚拟的App.Config文件有一个bindingRedirect条目对于每个冲突的程序集,使最高版本选择装配。这样做的后果是,永远不会有关于冲突程序集的警告,因为每个冲突都会
当为true时,每个不同的重新映射将导致高优先级注释显示旧版本和新版本,AutoUnify是真实的。
最后,如果你想观察"失败",你可以用以下参数调用msbuild:
msbuild /v:d /t:build /p:AutoUnifyAssemblyReferences=false;AutoGenerateBindingRedirects=false
为什么它工作?
因为你已经指定了;)
如何使它不工作?
- 右键单击解决方案资源管理器 中的DLL
- 选择属性
- 选择使用特定版本
附加问题:在构建过程中会发生什么?外部依赖的版本不是硬编码到主依赖吗?
除非您指定,否则不能。默认是关闭的。设计良好的程序集应该是向后兼容的,版本并不重要。
为了版本控制的目的,运行库区分常规程序集和强命名程序集。版本检查只对强命名程序集进行。
(来源:https://learn.microsoft.com/en-us/dotnet/framework/app-domains/assembly-versioning)
因此,答案如下:
为什么它工作?
因为程序集不是强命名的
如何使它不工作?
使程序集强命名为
取决于在构建过程中发生了什么?
-
Use specific version
= false:编译与项目中引用匹配的第一个文件,接受任何版本 -
Use specific version
= true:编译第一个匹配参考的文件,包括项目 中指定的版本
外部依赖的版本不是硬编码到主依赖吗?
是的,引用的程序集的版本是硬编码的。您可以通过使用反编译器(例如ILSpy)来读取这些信息