有三个程序集版本属性。有什么区别?如果我使用AssemblyVersion
并忽略其余部分可以吗?
MSDN 说:
组装版本:
指定被赋予属性的程序集的版本。
-
指示编译器为 Win32 文件版本资源使用特定版本号。Win32 文件版本不需要与程序集的版本号相同。
-
定义程序集清单的附加版本信息。
这是对使用程序集属性的最佳实践是什么的后续行动?
有三个程序集版本属性。有什么区别?如果我使用AssemblyVersion
并忽略其余部分可以吗?
MSDN 说:
组装版本:
指定被赋予属性的程序集的版本。
指示编译器为 Win32 文件版本资源使用特定版本号。Win32 文件版本不需要与程序集的版本号相同。
定义程序集清单的附加版本信息。
这是对使用程序集属性的最佳实践是什么的后续行动?
汇编版本
引用您的程序集的其他程序集将在哪里查找。如果此编号发生更改,其他程序集必须更新它们对您程序集的引用!仅当它破坏向后兼容性时才更新此版本。AssemblyVersion
是必需的。
我使用格式:major.minor(对于非常稳定的代码库来说是major )。这将导致:
[assembly: AssemblyVersion("1.3")]
如果您严格遵循SemVer,则这意味着您仅在主要更改时才更新,例如 1.0、2.0、3.0 等。
程序集文件版本
用于部署(如安装程序)。您可以为每个部署增加此数字。使用它来标记具有相同AssemblyVersion
但从不同构建和/或代码生成的程序集。
在 Windows 中,可以在文件属性中查看。
AssemblyFileVersion 是可选的。如果未给出,则使用 AssemblyVersion。
我使用格式:major.minor.patch.build,前三部分遵循SemVer,最后一部分使用 buildserver 的 buildnumber(0 表示本地构建)。这将导致:
[assembly: AssemblyFileVersion("1.3.2.42")]
请注意System.Version将这些部分命名为major.minor.build.revision
!
汇编信息版本
程序集的产品版本。这是您在与客户交谈或在您的网站上显示时使用的版本。此版本可以是一个字符串,例如“ 1.0 Release Candidate ”。
AssemblyInformationalVersion
是可选的。如果未给出,则使用 AssemblyFileVersion。
我使用格式:major.minor[.patch] [revision as string]。这将导致:
[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")]
按照惯例,版本的四个部分分别称为Major Version、Minor Version、Build和Revision。
AssemblyFileVersion
旨在唯一标识单个程序集的构建通常,您将手动设置 Major 和 Minor AssemblyFileVersion 以反映程序集的版本,然后在每次构建系统编译程序集时增加 Build 和/或 Revision。AssemblyFileVersion 应该允许您唯一标识程序集的构建,以便您可以将其用作调试任何问题的起点。
在我当前的项目中,我们让构建服务器将源代码控制存储库中的更改列表编号编码到 AssemblyFileVersion 的构建和修订部分中。这允许我们直接从程序集映射到其源代码,用于构建服务器生成的任何程序集(无需在源代码控制中使用标签或分支,或手动保留任何已发布版本的记录)。
此版本号存储在 Win32 版本资源中,可以在查看程序集的 Windows 资源管理器属性页时看到。
CLR 不关心也不检查 AssemblyFileVersion。
AssemblyInformationalVersion
旨在代表您的整个产品的版本AssemblyInformationalVersion 旨在允许对整个产品进行连贯的版本控制,该产品可能包含许多独立版本的程序集,可能具有不同的版本控制策略,并且可能由不同的团队开发。
“例如,一个产品的 2.0 版本可能包含多个程序集;其中一个程序集被标记为 1.0 版,因为它是未在同一产品的 1.0 版中发布的新程序集。通常,您设置此版本号的主要和次要部分来表示您的产品的公共版本。然后,每次打包完整产品及其所有组件时,都会增加构建和修订部分。” — Jeffrey Richter,[CLR via C#(第二版)] p。57
CLR 不关心也不检查 AssemblyInformationalVersion。
AssemblyVersion
是 CLR 关心的唯一版本(但它关心整个AssemblyVersion
)CLR 使用 AssemblyVersion 绑定到强命名程序集。它存储在已构建程序集的 AssemblyDef 清单元数据表中,以及引用它的任何程序集的 AssemblyRef 表中。
这非常重要,因为这意味着当您引用一个强命名程序集时,您将紧密绑定到该程序集的特定 AssemblyVersion。整个 AssemblyVersion 必须完全匹配才能使绑定成功。例如,如果您在构建时引用了强命名程序集的 1.0.0.0 版本,但在运行时只有该程序集的 1.0.0.1 版本可用,则绑定将失败!(然后您必须使用Assembly Binding Redirection来解决这个问题。)
AssemblyVersion
对整体是否必须匹配感到困惑。(是的,它确实。)关于整个 AssemblyVersion 是否必须完全匹配才能加载程序集存在一些混淆。有些人错误地认为只有 AssemblyVersion 的主要部分和次要部分必须匹配才能使绑定成功。这是一个合理的假设,但它最终是不正确的(从 .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.
我认为这种混淆的根源可能是因为微软最初打算对完整的 AssemblyVersion 的这种严格匹配更宽松一点,只匹配主要和次要版本部分:
“加载程序集时,CLR 将自动查找与所请求程序集的主要/次要版本匹配的最新安装的服务版本。” — Jeffrey Richter,[CLR via C#(第二版)] p。56
这是 1.0 CLR 的 Beta 1 中的行为,但是此功能在 1.0 版本之前被删除,并且没有设法在 .NET 2.0 中重新出现:
“注意:我刚刚描述了你应该如何看待版本号。不幸的是,CLR 不以这种方式处理版本号。[在 .NET 2.0 中],CLR 将版本号视为不透明值,如果程序集依赖于另一个程序集的 1.2.3.4 版本,则 CLR 尝试仅加载 1.2.3.4 版本(除非绑定重定向到位)。但是, Microsoft 计划在未来版本中更改 CLR 的加载程序,以便它加载给定主要/次要程序集版本的最新构建/修订版. 例如,在 CLR 的未来版本中,如果加载程序试图查找程序集的 1.2.3.4 版本并且版本 1.2.5.0 存在,则加载程序会自动选择最新的服务版本。这对 CLR 的加载器来说将是一个非常受欢迎的变化——我等不及了。” — Jeffrey Richter,[CLR via C#(第二版)] p。164(重点是我的)
由于此更改仍未实施,我认为可以肯定地假设 Microsoft 已经回溯了此意图,现在更改此内容可能为时已晚。我试图在网上搜索以了解这些计划发生了什么,但我找不到任何答案。我仍然想深入了解它。
所以我给杰夫·里希特发了电子邮件,直接问他——我想如果有人知道发生了什么,那就是他。
他在 12 小时内回复,同样是在星期六早上,并澄清说 .NET 1.0 Beta 1 加载程序确实实现了这种“自动前滚”机制来获取程序集的最新可用构建和修订,但这种行为是在 .NET 1.0 发布之前恢复。后来它打算重振它,但在 CLR 2.0 发布之前它没有实现。然后是 Silverlight,它优先考虑 CLR 团队,所以这个功能被进一步延迟。与此同时,在 CLR 1.0 Beta 1 时代周围的大多数人都已经继续前进,因此尽管已经投入了所有艰苦的工作,但它不太可能看到曙光。
当前的行为似乎将继续存在。
从我与 Jeff 的讨论中还值得注意的是,AssemblyFileVersion 仅在删除“自动前滚”机制后才添加 - 因为在 1.0 Beta 1 之后,对 AssemblyVersion 的任何更改对您的客户来说都是一个重大更改,然后无处可以安全地存储您的内部版本号。AssemblyFileVersion 是安全的避风港,因为 CLR 永远不会自动检查它。也许这样更清楚,有两个不同的版本号,具有不同的含义,而不是试图在 AssemblyVersion 的主要/次要(破坏)和构建/修订(非破坏)部分之间进行分离。
AssemblyVersion
寓意是,如果您要交付其他开发人员将要引用的程序集,则需要非常小心何时更改(和不)更改这些程序集的 AssemblyVersion。对 AssemblyVersion 的任何更改都意味着应用程序开发人员要么必须针对新版本重新编译(以更新那些 AssemblyRef 条目),要么使用程序集绑定重定向来手动覆盖绑定。
再看一下 mscorlib 上的版本属性:
// 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 部分,告诉您您正在使用哪个 Service Pack),同时 AssemblyVersion 固定在一个无聊的旧 2.0.0.0。对 AssemblyVersion 的任何更改都会强制每个引用 mscorlib.dll 的 .NET 应用程序针对新版本重新编译!
AssemblyVersion
几乎保持在 .NET 内部,而AssemblyFileVersion
Windows 所看到的是。如果您转到位于目录中的程序集的属性并切换到版本选项卡,AssemblyFileVersion
那么您将在顶部看到。如果您按版本对文件进行排序,这就是资源管理器所使用的。
映射到“AssemblyInformationalVersion
产品版本”并且是纯粹的“人类使用”。
AssemblyVersion
当然是最重要的,但我也不会跳过AssemblyFileVersion
。如果您不提供AssemblyInformationalVersion
,编译器会通过剥离版本号的“修订”部分并保留major.minor.build 来为您添加它。
AssemblyInformationalVersion
并AssemblyFileVersion
在您通过 Windows 资源管理器查看文件属性查看文件的“版本”信息时显示。这些属性实际上被编译到VERSION_INFO
由编译器创建的资源中。
AssemblyInformationalVersion
是“产品版本”值。AssemblyFileVersion
是“文件版本”值。
AssemblyVersion
特定于 .NET 程序集,.NET 程序集加载器使用它来了解在运行时加载/绑定哪个版本的程序集。
其中,.NET 绝对需要的唯一一个是AssemblyVersion
属性。不幸的是,当它不加选择地更改时,它也可能导致大多数问题,特别是如果您对程序集进行强命名。
为了使这个问题保持最新,值得强调的AssemblyInformationalVersion
是 NuGet 使用并反映了包版本,包括任何预发布后缀。
例如,使用 asp.net 核心 dotnet-cli 打包的 AssemblyVersion 为 1.0.3.*
dotnet pack --version-suffix ci-7 src/MyProject
生成一个版本为 1.0.3-ci-7 的包,您可以使用以下方法通过反射进行检查:
CustomAttributeExtensions.GetCustomAttribute<AssemblyInformationalVersionAttribute>(asm);
值得注意的是其他一些事情:
如生成的程序集文件的 Windows 资源管理器属性对话框中所示,有两个地方称为“文件版本”。对话框标题中显示的是 AssemblyVersion,而不是 AssemblyFileVersion。
在其他版本信息部分,还有另一个元素称为“文件版本”。在这里您可以看到作为 AssemblyFileVersion 输入的内容。
AssemblyFileVersion 只是纯文本。它不必符合 AssemblyVersion 所做的编号方案限制(例如,<build> < 65K)。如果您愿意,它可以是 3.2.<release tag text>.<datetime>。您的构建系统将必须填写令牌。
此外,它不受 AssemblyVersion 的通配符替换。如果您在 AssemblyInfo.cs 中只有一个值“3.0.1.*”,那么这正是其他版本信息 -> 文件版本元素中将显示的内容。
不过,我不知道使用数字文件版本号以外的其他内容对安装程序的影响。
当程序集的 AssemblyVersion 更改时,如果它具有强名称,则需要重新编译引用程序集,否则程序集不会加载!如果它没有强名称,如果没有显式添加到项目文件中,则在构建时不会将其复制到输出目录,因此您可能会错过依赖程序集,尤其是在清理输出目录之后。