AssemblyVersion、AssemblyFileVersion和AssemblyInformationalVersion之间有什么区别?

有三个汇编版本属性。有什么区别?如果我使用AssemblyVersion而忽略其余的是否可以?


MSDN说:

  • 组件版本

    指定要归因的程序集的版本。

  • 组件文件版本

    指示编译器为Win32文件版本资源使用特定版本号。Win32文件版本不需要与程序集的版本号相同。

  • 汇编信息版

    定义程序集清单的其他版本信息。


这是使用程序集属性的最佳实践是什么?

210134 次浏览

AssemblyVersion基本上保持在. NET内部,而AssemblyFileVersion是Windows看到的。如果您转到目录中的程序集的属性并切换到版本选项卡,您将在顶部看到AssemblyFileVersion。如果您按版本对文件进行排序,这就是资源管理器使用的内容。

AssemblyInformationalVersion映射到“产品版本”,意味着纯粹是“人类使用的”。

AssemblyVersion当然是最重要的,但我也不会跳过AssemblyFileVersion。如果你没有提供AssemblyInformationalVersion,编译器会通过剥离版本号的“修订”部分并留下major.minor.build.来为你添加它

当您通过Windows资源管理器通过查看文件属性查看文件的“版本”信息时,会显示AssemblyInformationalVersionAssemblyFileVersion。这些属性实际上被编译到编译器创建的VERSION_INFO资源中。

AssemblyInformationalVersion是“产品版本”值。AssemblyFileVersion是“文件版本”值。

AssemblyVersion特定于. NET程序集,并由. NET程序集加载程序使用以了解要在运行时加载/绑定的程序集版本。

其中,. NET唯一绝对需要的是AssemblyVersion属性。不幸的是,当它不分青红皂白地更改时,它也会导致大多数问题,尤其是在您对程序集进行强命名的情况下。

组件版本

引用您的程序集的其他程序集将在哪里查看。如果此编号更改,其他程序集必须更新其对您程序集的引用!仅在破坏向后兼容性时更新此版本。AssemblyVersion是必需的。

我使用格式:major.minor(和主要用于非常稳定的代码库)。这将导致:

[assembly: AssemblyVersion("1.3")]

如果您严格遵循SemVer,那么这意味着您仅在主要更改时更新,因此1.0,2.0,3.0等。

组件文件版本

用于部署(如安装程序)。您可以为每次部署增加此数字。使用它来标记具有相同AssemblyVersion但从不同构建和/或代码生成的程序集。

在Windows中,它可以在文件属性中查看。

AssemblyFileVersion是可选的。如果未给出,则使用AssemblyVersion。

我使用格式:major.minor.patch.build,其中前三部分遵循SemVer,最后一部分使用Buildserver的构建号(本地构建为0)。这将导致:

[assembly: AssemblyFileVersion("1.3.2.42")]

请注意,系统版本将这些部分命名为major.minor.build.revision

汇编信息版

程序集的产品版本。这是您在与客户交谈或在您的网站上显示时使用的版本。此版本可以是一个字符串,例如“1.0候选版本”。

AssemblyInformationalVersion是可选的。如果没有给出,则使用AssemblyFileVersion。

我使用格式:major.minor[. patch][修订为字符串]。这将导致:

[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旨在唯一标识个人集会的构建

通常,您将手动设置主要和次要AssemblyFileVersion以反映程序集的版本,然后在每次构建系统编译程序集时递增构建和/或修订。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可用,则绑定将失败!(然后您必须使用程序集绑定重定向解决此问题。)

混淆是否整个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=0b3305902db7183fSuccessfully 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=0b3305902db7183fAssembly 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 definitiondoes 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\DaniLOG: DisplayName = Rhino.Mocks, Version=3.5.0.1336, Culture=neutral, PublicKeyToken=0b3305902db7183f(Fully-specified)LOG: Appbase = [...]LOG: Initial PrivatePath = NULLCalling 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=0b3305902db7183fLOG: Attempting download of new URL [...].WRN: Comparing the assembly name resulted in the mismatch: Revision NumberERR: 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不能治疗版本号以这种方式。[在. NET2.0],CLR将版本号视为不透明值,如果程序集取决于另一个版本1.2.3.4程序集,CLR尝试加载仅1.2.3.4版本(除非绑定重定向已到位)。但是,微软计划改变CLR的加载器在未来的版本,所以它装的是最新的构建/修改给定的主要/次要程序集的版本。例如,在CLR的未来版本中,如果加载器正在尝试查找版本1.2.3.4程序集和版本1.2.5.0存在,加载程序会自动获取最新的服务版本。这将是一个非常欢迎更改CLR的加载程序-I一个不能等”。-Jeffrey Richter,[CLR via C#(第二版)]第164页(强调)我的)

由于这一变化还没有实施,我认为可以肯定的是,微软已经背离了这一意图。现在改变这一点可能为时已晚。我试图在网上搜索这些计划的结果,但我找不到任何答案。我仍然想弄清楚它。

所以我给杰夫·里希特发了电子邮件,直接问他——我想如果有人知道发生了什么,那就是他。

他在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条目)或使用程序集绑定重定向手动覆盖绑定。

  • 不要更改服务版本的AssemblyVersion,该版本旨在向后兼容。
  • 更改您知道有重大更改的发布的AssemblyVersion。

只需再看一下mcorlib上的版本属性:

// 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包含了所有有趣的服务信息(正是这个版本的修订版部分告诉您所使用的Service Pack),同时AssemblyVersion固定在一个无聊的旧2.0.0.0。对AssemblyVersion的任何更改都会强制每个. NET应用程序引用mscorlib.dll针对新版本重新编译!

当程序集的AssemblyVersion更改时,如果它具有强名称,则需要重新编译引用程序集,否则程序集不会加载!如果它没有强名称,如果没有显式添加到项目文件中,则在构建时不会将其复制到输出目录,因此您可能会错过依赖程序集,尤其是在清理输出目录之后。

值得注意的是其他一些事情:

  1. 如生成的程序集文件的Windows资源管理器属性对话框所示,有两个地方称为“文件版本”。在对话框标题中看到的那个显示AssemblyVersion,而不是AssemblyFileVersion。

    在其他版本信息部分中,有另一个名为“文件版本”的元素。您可以在此处查看作为AssemblyFileVersion输入的内容。

  2. AssemblyFileVersion只是纯文本。它不必符合AssemblyVersion所做的编号方案限制(<65K,例如)。它可以是3.2。<发布标签文本>。,如果你愿意。您的构建系统必须填写令牌。

    此外,它不受AssemblyVersion的通配符替换的约束。如果您在AssemblyInfo.cs中只有一个值“3.0.1.*”,这正是将在其他版本信息->文件版本元素中显示的内容。

  3. 不过,我不知道使用数字文件版本号以外的东西对安装程序的影响。

为了使这个问题保持最新,值得强调的是,NuGet使用AssemblyInformationalVersion并反映包版本,包括任何预发布后缀。

例如,使用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);