我正在寻找有关如何管理 .NET 程序集的三个不同程序集版本号的指针、建议甚至口述。产品版本是最简单的,因为这似乎通常由业务决定。然后,文件版本似乎用于部署之间的版本控制,其中实际的程序集版本仅在发布时使用。
现在我只是在寻找一种简单的方法来标记一个不依赖的程序集的测试和维护版本,所以我正在查看文件版本上自动递增的构建和修订号,对于最终版本,复制当前文件版本到程序集版本。该产品在生产中使用,但仍在开发中 - 你知道 - 那些小公司之一,没有变更控制基础设施的情况。
我正在寻找有关如何管理 .NET 程序集的三个不同程序集版本号的指针、建议甚至口述。产品版本是最简单的,因为这似乎通常由业务决定。然后,文件版本似乎用于部署之间的版本控制,其中实际的程序集版本仅在发布时使用。
现在我只是在寻找一种简单的方法来标记一个不依赖的程序集的测试和维护版本,所以我正在查看文件版本上自动递增的构建和修订号,对于最终版本,复制当前文件版本到程序集版本。该产品在生产中使用,但仍在开发中 - 你知道 - 那些小公司之一,没有变更控制基础设施的情况。
版本控制是我非常热衷的事情,并且花了很长时间试图提出一个易于使用的版本控制系统。根据您在问题中已经说过的内容,很明显您已经理解了一个重要点,程序集版本号与产品版本不是同义词。一是技术驱动,二是业务驱动。
以下假设您使用某种形式的源代码管理和构建服务器。对于上下文,我们使用TeamCity和 Subversion/Git。TeamCity 对一小部分 (10) 项目是免费的,并且是一个非常好的构建服务器,但还有其他一些,其中一些是完全免费的。
一个版本对一个人意味着什么可能对另一个人意味着不同的东西,一般结构是主要的,次要的,宏观的,微观的。我查看版本号的方式是将其分为两部分。前半部分描述了主要版本(Major)和任何关键更新(Minor)。后半部分表示它是何时构建的以及源代码版本是什么。版本号也意味着不同的东西,具体取决于上下文,它是 API、Web 应用程序等。
Major
. Minor
. Build
.Revision
Revision
这是从源代码控制中获取的数字,用于识别实际构建的内容。Build
这是一个不断增加的数字,可用于在构建服务器上查找特定构建。这是一个重要的数字,因为构建服务器可能使用不同的参数集构建了两次相同的源。将内部版本号与源代码一起使用可以让您确定构建的内容和方式。Minor
只有当公共接口发生重大变化时,这才应该改变。例如,如果它是一个 API,那么消费代码仍然能够编译吗?当主要编号更改时,此编号应重置为零。Major
指示您使用的产品版本。例如,所有 VisualStudio 2008 程序集的 Major 是 9,VisualStudio 2010 是 10。规则总是有例外的,当你遇到它们时你必须适应。我最初的方法是基于使用颠覆,但最近我已经转向 Git。使用中央存储库的源代码控制(如 subversion 和源代码安全)有一个编号,可用于识别给定时间的特定源集。对于像 Git 这样的分布式源代码控制,情况并非如此。因为 Git 使用每个开发机器上的分布式存储库,所以没有可以使用的自动递增数字,有一个使用签入数量的 hack,但它很难看。正因为如此,我不得不改进我的方法。
Major
. Minor
. Macro
.Build
修订号现在已经消失了,构建已经转移到以前的修订版并且插入了宏。您可以使用您认为合适的宏,但大多数时候我不理会它。因为我们使用 TeamCity,所以可以在构建中找到从修订号丢失的信息,这确实意味着有一个两步过程,但我们没有丢失任何东西,这是一个可以接受的折衷方案。
首先要了解的是,Assembly Version、File Version 和 Product Version 不一定要匹配。我不提倡使用不同的数字集,但是当对不影响任何公共接口的程序集进行小的更改时,它会使生活变得更轻松,您不必重新编译依赖程序集。我处理这个问题的方法是只在 Assembly Version 中设置 Major 和 Minor 数字,但在 File Version 中设置所有值。例如:
这使您能够推出不会破坏现有代码的热修复程序,因为程序集版本不匹配,但允许您通过查看程序集的文件版本号来查看程序集的修订/构建。这是一种常见的方法,当您查看程序集详细信息时,可以在一些开源程序集上看到。
作为团队负责人,您需要负责在需要进行重大更改时增加次要编号。对界面进行必要的更改但不破坏以前的代码的一种解决方案是将当前代码标记为过时并创建一个新界面。这意味着警告现有代码该方法已过时并且可以随时删除,但不需要您立即破坏所有内容。然后,您可以在迁移所有内容后删除过时的方法。
您可以手动完成上述所有操作,但这会非常耗时,以下是我们如何自动化该过程。每个步骤都是可运行的。
AssemblyVersion
和AssemblyFileVersion
属性。AssemblyVersion
和属性。AssemblyFileVersion
颠覆:
<PropertyGroup>
<Version-Major>0</Version-Major>
<Version-Minor>0</Version-Minor>
<Version-Build Condition=" '$(build_number)' == '' ">0</Version-Build>
<Version-Build Condition=" '$(build_number)' != '' ">$(build_number)</Version-Build>
<Version-Revision Condition=" '$(revision_number)' == '' ">0</Version-Revision>
<Version-Revision Condition=" '$(revision_number)' != '' ">$(revision_number)</Version-Revision>
</PropertyGroup>
希望我已经清楚了,但涉及很多。请提出任何问题。我将使用任何反馈将更简洁的博客文章放在一起。
[AssemblyVersion] 在 .NET 中非常重要。微软鼓励的一种理念是让它自动递增,强制重新编译依赖于程序集的所有项目。如果您使用构建服务器,则可以正常工作。做这件事从来没有错,但要小心携带剑的人。
另一个与它的实际含义更密切相关的是,该数字代表了程序集公共接口的版本控制。换句话说,只有在更改公共接口或类时才能更改它。因为只有这样的更改才需要重新编译程序集的客户端。不过,这需要手动完成,构建系统不够智能,无法自动检测到此类更改。
您可以通过仅在将程序集部署在您无法触及的机器上时增加版本来进一步扩展此方法。这是 Microsoft 使用的方法,他们的 .NET 程序集版本号很少更改。主要是因为它给他们的客户带来了相当大的痛苦。
所以微软宣扬的不是它实践的。然而,它的构建过程和版本控制是无与伦比的,他们甚至有一个专门的软件工程师来监控这个过程。效果不太好,WaitHandle.WaitOne(int) 重载尤其引起了相当多的痛苦。在 .NET 4.0 中使用非常不同的方法进行了修复,但这有点超出了范围。
这取决于您和您对控制构建过程和发布周期的信心来做出自己的选择。除此之外,自动增加 [AssemblyFileVersion] 是非常合适的。然而,不支持这不便。
您可以使用版本号的 Build 部分进行自动增量。
[assembly: AssemblyVersion("1.0.*")]
在您的环境中,测试版本是具有构建版本!= 0 的版本。在发布时,您增加次要部分并将构建部分设置为 0,这就是您识别已发布程序集的方式。
如果您在 GAC 中安装程序集,随着时间的推移,您的 GAC 会充斥着大量不同的版本,因此请记住这一点。但是如果你只在本地使用 dll,我认为这是一个很好的做法。
添加到Bronumskis 答案中,我想指出,在semver.orgMajor.Minor.Build.Revision
的语义版本控制 2.0 标准之后,由于增加数字后必须将右侧的所有常规值重置为零的规则,这将是非法的。
遵循标准的更好方法是使用Major.Minor+Build.Revision
. 这显然不适用于 in AssemblyVersionAttribute
,但可以使用自定义属性或静态类。
TeamCity 中的 Semver 应该可以使用 Meta-runner Power Pack 获得。对于带有 git-flow 的 git(尤其是在 .NET 世界中),我发现GitVersion很有帮助。
在版本控制程序集方面没有硬性规定,因此请随意尝试适合您的方法,但我建议您使用 4 部分方法,因为您将拥有灵活性,以防您想要进行一些更改在将来。
...例如:1.0.0.*
保留 - 这增加了额外的灵活性,以防您将来想要进行任何更改。但默认情况下,将其保持为 0。
此外,请考虑使用强密钥对程序集进行签名。如果您在 GAC 中注册了多个版本的程序集,这将解决程序集冲突问题。 MSDN 链接
这是一个用于自动生成程序集信息的 T4 模板示例。每次执行转换时,程序集版本都会递增。您应该只填写您的项目数据:
// Names.
string projectName = "MyProjectName"; // Project (short name for COM).
string productName = "My Project Name"; // Full title.
string developerName = "Developer Name"; // Developer.
string assemblyType = "Application"; // Application, Library, etc.
// Version.
int majorVersion = 1;
int minorVersion = 0;
// Year of the start of work on the project.
string since = "2021";
转换模板后,您将获得如下输出:
#if COMVISIBLE
using System.EnterpriseServices;
#endif
using System.Reflection;
using System.Runtime.InteropServices;
// General information about this assembly is provided by the following set
// attributes. Change the values of these attributes to change the information,
// related to the assembly.
[assembly: AssemblyTitle ("My Project Name Application 1.0")] // Assembly name.
[assembly: AssemblyDescription ("My Project Name Application 1.0")] // Assembly description.
[assembly: AssemblyCompany ("Developer Name")] // Developer.
[assembly: AssemblyProduct ("Developer Name My Project Name Application")] // Product name.
[assembly: AssemblyCopyright ("© Developer Name 2021")] // Copyright.
//[assembly: AssemblyTrademark ("Developer Name ® My Project Name Application®")] // Trademark.
[assembly: AssemblyCulture ("")]
[assembly: AssemblyVersion ("1.0.2110.0047")]
[assembly: AssemblyFileVersion ("1.0.2110.0047")]
#if DEBUG
[assembly: AssemblyConfiguration ("Debug")]
#else
[assembly: AssemblyConfiguration ("Release")]
#endif
// Setting ComVisible to False makes the types in this assembly invisible
// for COM components. If you need to refer to the type in this assembly via COM,
// set the ComVisible attribute to TRUE for this type.
#if COMVISIBLE
[assembly: ComVisible (true)]
[assembly: ApplicationName ("MyProjectName")] // The name of the COM application.
[assembly: ApplicationID ("fc24620a-239d-4e40-b756-7ed38e82ef69")]
#else
[assembly: ComVisible (false)]
#endif
// The following GUID is used to identify the type library if this project will be visible to COM
[assembly: Guid ("e60d1ecf-6c7b-4c9b-925f-4bf07615da87")]