有三个程序集版本属性。什么是差异?是否可以使用AssemblyVersion而忽略其余部分?
MSDN说:
AssemblyVersion: 指定指定属性的程序集的版本。 AssemblyFileVersion: 指示编译器为Win32文件版本资源使用特定的版本号。Win32文件版本不需要与程序集的版本号相同。 AssemblyInformationalVersion: 定义程序集清单的其他版本信息。
本文是“使用程序集属性的最佳实践是什么?”
有三个程序集版本属性。什么是差异?是否可以使用AssemblyVersion而忽略其余部分?
MSDN说:
AssemblyVersion: 指定指定属性的程序集的版本。 AssemblyFileVersion: 指示编译器为Win32文件版本资源使用特定的版本号。Win32文件版本不需要与程序集的版本号相同。 AssemblyInformationalVersion: 定义程序集清单的其他版本信息。
本文是“使用程序集属性的最佳实践是什么?”
AssemblyVersion基本上是。net内部的,而AssemblyFileVersion是Windows所看到的。如果您转到位于目录中的程序集的属性并切换到版本选项卡,您将在顶部看到AssemblyFileVersion。如果您按版本对文件进行排序,这就是Explorer所使用的。
AssemblyInformationalVersion映射到“产品版本”,并意味着纯粹是“人类使用的”。
AssemblyVersion当然是最重要的,但我也不会跳过AssemblyFileVersion。如果您没有提供AssemblyInformationalVersion,编译器将为您添加它,方法是去掉版本号的“revision”部分,并保留major.minor.build。
当您通过Windows资源管理器通过查看文件属性查看文件的“版本”信息时,会显示AssemblyInformationalVersion和AssemblyFileVersion。这些属性实际上被编译到编译器创建的VERSION_INFO资源中。
AssemblyInformationalVersion是“产品版本”值。AssemblyFileVersion是“文件版本”值。
AssemblyVersion是特定于. net程序集的,. net程序集加载器使用它来了解在运行时加载/绑定程序集的哪个版本。
在这些属性中,. net唯一绝对需要的是AssemblyVersion属性。不幸的是,当它不加区别地更改时,也会导致大多数问题,特别是如果您对程序集进行了强命名的话。
AssemblyVersion
引用您程序集的其他程序集将查找的位置。如果此编号更改,其他程序集必须更新它们对您的程序集的引用!只有当这个版本破坏向后兼容性时才更新它。需要AssemblyVersion。
我使用的格式是:major。Minor(对于非常稳定的代码库,major)。这将导致:
[assembly: AssemblyVersion("1.3")]
如果您严格遵循SemVer,那么这意味着您只在主要更改时进行更新,即1.0、2.0、3.0等。
AssemblyFileVersion
用于部署(如安装程序)。您可以为每次部署增加这个数字。使用它来标记具有相同的AssemblyVersion但由不同的构建和/或代码生成的程序集。
在Windows中,可以在文件属性中查看。
AssemblyFileVersion是可选的。如果没有给出,则使用AssemblyVersion。
我使用的格式是:major.minor.patch。在这里,我使用SemVer完成前三个部分,使用构建服务器的buildnumber完成最后一个部分(本地构建为0)。 这将导致:
[assembly: AssemblyFileVersion("1.3.2.42")]
注意系统。Version将这些部分命名为major.minor.build.revision!
AssemblyInformationalVersion
程序集的“产品”版本。这是你在与客户交谈或在你的网站上展示时使用的版本。这个版本可以是一个字符串,比如'1.0 Release Candidate'。
AssemblyInformationalVersion是可选的。如果没有给出,则使用AssemblyFileVersion。
我使用的格式是:major.minor[。补丁][修订为字符串]。这将导致:
[assembly: AssemblyInformationalVersion("1.3 RC1")]
鉴于目前至少有三种方法可以为程序集指定版本,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")]
按照惯例,版本的四个部分被称为主要版本、次要版本、构建和修订。
AssemblyFileVersion用于惟一地标识单个程序集的构建
通常,您将手动设置Major和Minor AssemblyFileVersion以反映程序集的版本,然后在每次构建系统编译程序集时增加Build和/或Revision。AssemblyFileVersion应该允许您唯一地标识程序集的构建,以便您可以将其用作调试任何问题的起点。
在我当前的项目中,我们让构建服务器将来自源代码控制存储库的更改列表编号编码到AssemblyFileVersion的build和Revision部分。对于构建服务器生成的任何程序集,这允许我们直接从程序集映射到它的源代码(不必在源代码控制中使用标签或分支,或手动保存发布版本的任何记录)。
此版本号存储在Win32版本资源中,在查看程序集的Windows资源管理器属性页时可以看到。
CLR不关心也不检查AssemblyFileVersion。
AssemblyInformationalVersion用于表示整个产品的版本
AssemblyInformationalVersion旨在允许对整个产品进行一致的版本控制,该产品可能由许多独立版本控制的程序集组成,这些程序集可能具有不同的版本控制策略,并且可能由不同的团队开发。
“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
CLR不关心也不检查AssemblyInformationalVersion。
装配版本是CLR唯一关心的版本(但它关心整个装配版本)
CLR使用AssemblyVersion绑定到强命名程序集。它存储在已构建程序集的AssemblyDef清单元数据表中,以及引用它的任何程序集的AssemblyRef表中。
这非常重要,因为这意味着当您引用强命名程序集时,您将被紧密绑定到该程序集的特定AssemblyVersion。为了绑定成功,整个AssemblyVersion必须完全匹配。例如,如果在构建时引用强命名程序集的1.0.0.0版本,但在运行时只有该程序集的1.0.0.1版本可用,则绑定将失败!(然后必须使用程序集绑定重定向来解决这个问题。)
不清楚整个AssemblyVersion是否必须匹配。(是的,确实如此。)
对于整个AssemblyVersion是否必须完全匹配才能加载程序集,有一点困惑。有些人错误地认为,只有AssemblyVersion的Major和Minor部分必须匹配才能使绑定成功。这是一个合理的假设,但它最终是不正确的(从。net 3.5开始),并且为您的CLR版本验证这一点很简单。只需执行这个示例代码。
在我的机器上,第二次组装加载失败了,融合日志的最后两行非常清楚地说明了原因:
.NET Framework Version: 2.0.50727.3521
---
Attempting to load assembly: Rhino.Mocks, Version=3.5.0.1337, Culture=neutral, PublicKeyToken=0b3305902db7183f
Successfully loaded assembly: Rhino.Mocks, Version=3.5.0.1337, Culture=neutral, PublicKeyToken=0b3305902db7183f
---
Attempting to load assembly: Rhino.Mocks, Version=3.5.0.1336, Culture=neutral, PublicKeyToken=0b3305902db7183f
Assembly binding for failed:
System.IO.FileLoadException: Could not load file or assembly 'Rhino.Mocks, Version=3.5.0.1336, Culture=neutral,
PublicKeyToken=0b3305902db7183f' or one of its dependencies. The located assembly's manifest definition
does not match the assembly reference. (Exception from HRESULT: 0x80131040)
File name: 'Rhino.Mocks, Version=3.5.0.1336, Culture=neutral, PublicKeyToken=0b3305902db7183f'
=== Pre-bind state information ===
LOG: User = Phoenix\Dani
LOG: DisplayName = Rhino.Mocks, Version=3.5.0.1336, Culture=neutral, PublicKeyToken=0b3305902db7183f
(Fully-specified)
LOG: Appbase = [...]
LOG: Initial PrivatePath = NULL
Calling assembly : AssemblyBinding, 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:\Windows\Microsoft.NET\Framework64\v2.0.50727\config\machine.config.
LOG: Post-policy reference: Rhino.Mocks, Version=3.5.0.1336, Culture=neutral, PublicKeyToken=0b3305902db7183f
LOG: Attempting download of new URL [...].
WRN: Comparing the assembly name resulted in the mismatch: Revision Number
ERR: Failed to complete setup of assembly (hr = 0x80131040). Probing terminated.
我认为这种混淆的来源可能是因为Microsoft最初打算对完整的AssemblyVersion的这种严格匹配更宽松一些,只匹配Major和Minor版本部分:
当加载程序集时,CLR将自动找到最新的 已安装的维修版本 类型的主/次版本 要求集合。” ——Jeffrey Richter, [CLR via c#(第二版)]第56页
这是1.0 CLR Beta 1的行为,但是这个特性在1.0版本之前就被删除了,并且在。net 2.0中没有重新出现:
“Note: I have just described how you should think of version numbers. Unfortunately, the CLR doesn’t treat version numbers this way. [In .NET 2.0], the CLR treats a version number as an opaque value, and if an assembly depends on version 1.2.3.4 of another assembly, the CLR tries to load version 1.2.3.4 only (unless a binding redirection is in place). However, Microsoft has plans to change the CLR’s loader in a future version so that it loads the latest build/revision for a given major/minor version of an assembly. For example, on a future version of the CLR, if the loader is trying to find version 1.2.3.4 of an assembly and version 1.2.5.0 exists, the loader with automatically pick up the latest servicing version. This will be a very welcome change to the CLR’s loader — I for one can’t wait.” — Jeffrey Richter, [CLR via C# (Second Edition)] p. 164 (Emphasis mine)
由于这一改变还没有实现,我认为微软已经放弃了这一意图,现在改变可能为时已晚。我试着在网上搜索,想知道这些计划发生了什么,但我找不到任何答案。我还是想一探究竟。
所以我给杰夫·里希特发了封邮件,直接问了他——我想如果有人知道发生了什么,那一定是他。
He replied within 12 hours, on a Saturday morning no less, and clarified that the .NET 1.0 Beta 1 loader did implement this ‘automatic roll-forward’ mechanism of picking up the latest available Build and Revision of an assembly, but this behaviour was reverted before .NET 1.0 shipped. It was later intended to revive this but it didn’t make it in before the CLR 2.0 shipped. Then came Silverlight, which took priority for the CLR team, so this functionality got delayed further. In the meantime, most of the people who were around in the days of CLR 1.0 Beta 1 have since moved on, so it’s unlikely that this will see the light of day, despite all the hard work that had already been put into it.
目前的行为似乎还会持续下去。
It is also worth noting from my discussion with Jeff that AssemblyFileVersion was only added after the removal of the ‘automatic roll-forward’ mechanism — because after 1.0 Beta 1, any change to the AssemblyVersion was a breaking change for your customers, there was then nowhere to safely store your build number. AssemblyFileVersion is that safe haven, since it’s never automatically examined by the CLR. Maybe it’s clearer that way, having two separate version numbers, with separate meanings, rather than trying to make that separation between the Major/Minor (breaking) and the Build/Revision (non-breaking) parts of the AssemblyVersion.
底线:在更改AssemblyVersion时仔细考虑
寓意是,如果您正在提供其他开发人员将要引用的程序集,那么您需要非常小心地确定何时更改(或不更改)这些程序集的AssemblyVersion。对AssemblyVersion的任何更改都意味着应用程序开发人员必须根据新版本重新编译(以更新那些AssemblyRef条目),或者使用程序集绑定重定向手动重写绑定。
不要为向后兼容的服务版本更改AssemblyVersion。 对于您知道有突破性变化的版本,请更改AssemblyVersion。
再看一下mscorlib中的version属性:
// 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")]
请注意,是AssemblyFileVersion包含了所有有趣的服务信息(这个版本的Revision部分告诉您您使用的是什么服务包),同时,AssemblyVersion被固定在一个无聊的旧2.0.0.0。对AssemblyVersion的任何更改都会迫使每个引用mscorlib.dll的.NET应用程序根据新版本重新编译!
当程序集的AssemblyVersion被更改时, 如果它具有强名称,则需要重新编译引用程序集,否则将无法加载程序集! 如果它没有强名称,如果没有显式地添加到项目文件中,它将不会在构建时复制到输出目录,因此您可能会错过依赖的程序集,特别是在清理输出目录之后。
还有一些值得注意的事情:
As shown in Windows Explorer Properties dialog for the generated assembly file, there are two places called "File version". The one seen in the header of the dialog shows the AssemblyVersion, not the AssemblyFileVersion. In the Other version information section, there is another element called "File Version". This is where you can see what was entered as the AssemblyFileVersion. AssemblyFileVersion is just plain text. It doesn't have to conform to the numbering scheme restrictions that AssemblyVersion does (<build> < 65K, e.g.). It can be 3.2.<release tag text>.<datetime>, if you like. Your build system will have to fill in the tokens. Moreover, it is not subject to the wildcard replacement that AssemblyVersion is. If you just have a value of "3.0.1.*" in the AssemblyInfo.cs, that is exactly what will show in the Other version information->File Version element. I don't know the impact upon an installer of using something other than numeric file version numbers, though.
为了使这个问题保持最新,值得强调的是,NuGet使用了AssemblyInformationalVersion,并反映了包含任何预发布后缀的包版本。
例如,1.0.3的AssemblyVersion。*与asp.net核心dotnet-cli打包
dotnet pack --version-suffix ci-7 src/MyProject
生成一个版本为1.0.3-ci-7的包,您可以使用以下方法进行反射检查:
CustomAttributeExtensions.GetCustomAttribute<AssemblyInformationalVersionAttribute>(asm);