如何拥有自动递增版本号(Visual Studio)?

esa*_*sac 454 c# versioning visual-studio

我想存储一组在构建时自动递增的整数:

int MajorVersion = 0;
int MinorVersion = 1;
int Revision = 92;
Run Code Online (Sandbox Code Playgroud)

当我编译时,它会自动增加Revision.当我构建安装项目时,它会增加MinorVersion(我可以手动执行此操作).MajorVersion只会手动增加.

然后我可以在菜单Help/About中向用户显示版本号:

  Version: 0.1.92

怎么能实现这一目标?

这个问题不仅要求如何使用自动递增版本号,还要求如何在代码中使用它,这是一个比其他更完整的答案.

Noe*_*edy 592

如果将AssemblyInfo类添加到项目中并将AssemblyVersion属性修改为以星号结尾,例如:

[assembly: AssemblyVersion("2.10.*")]
Run Code Online (Sandbox Code Playgroud)

Visual Studio会根据这些规则为你增加最终的数字(感谢galets,我完全错了!)

要在代码中引用此版本,以便可以将其显示给用户,请使用反射.例如,

Version version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
DateTime buildDate = new DateTime(2000, 1, 1)
                        .AddDays(version.Build).AddSeconds(version.Revision * 2);
string displayableVersion = $"{version} ({buildDate})";
Run Code Online (Sandbox Code Playgroud)

你应该知道的两个重要问题

来自@ ashes999:

另外值得一提的是,如果同时AssemblyVersionAssemblyFileVersion指定,你不会看到这对您的.exe文件.

来自@ BrainSlugs83:

仅设置第4个数字*可能不好,因为版本不会总是递增.第3个数字是自2000年以来的天数,第4个数字是自午夜以来的秒数(除以2)[不是随机数].因此,如果您在一天的最后一天构建解决方案,并在第二天的早些时候构建解决方案,则后一版本将具有较早的版本号.我建议总是使用X.Y.*而不是X.Y.Z.*因为你的版本号总是这样增加.

  • 仅将第4个数字设置为"*"可能不好,因为版本不会总是递增.第3个数字是自2000年以来的天数,第4个数字是自午夜以来的秒数(除以2)[不是随机数].因此,如果您在一天的最后一天构建解决方案,并在第二天的早些时候构建解决方案,则后一版本将具有较早的版本号.我建议总是使用"XY*"而不是"XYZ*",因为你的版本号总是会以这种方式增加(除非你碰巧在你的TARDIS里面编译代码 - 在这种情况下,我能来吗?). (156认同)
  • 值得注意的是,如果指定了'AssemblyVersion`和`AssemblyFileVersion`,你就不会在`.exe`上看到这个. (49认同)
  • 顺便说一下,你真的不需要编辑和添加汇编信息文件.更简单的方法是转到项目属性,应用程序选项卡,单击"装配信息"并输入主要版本,次要版本,然后在第三个框中输入*并将第4个框留空.Visual Studio将负责用这个更新.cs文件 (20认同)
  • 我们可以设定*开始的值吗?而不是使用自2000年以来的天数? (3认同)
  • 您应该如何将此更改恢复为源代码管理? (2认同)
  • 不确定它只是我还是VS2017,但是将AssemblyVersion调整为"XY*"而将AssemblyFileVersion保留为"XYZW"会导致奇怪的行为.只有在我注释掉AssemblyFileVersion之后,"XY*"开始按照预期的行为,如上所述,默认构建和修订号.我不确定他们是如何相关的. (2认同)
  • 这是时间戳,而不是版本号。如果您在 UAT 和 Release 中都有构建,则具有从未发布功能的 UAT 构建在某些情况下可能会具有比发布中的版本更小的版本。错误的。 (2认同)

小智 157

您可以使用Visual Studio中T4模板机制从简单的文本文件生成所需的源代码:

我想为某些.NET项目配置版本信息生成.自从我调查可用选项以来已经很长时间了,所以我一直在搜索,希望能找到一些简单的方法.我发现的内容并不令人鼓舞:人们编写Visual Studio加载项和自定义MsBuild任务只是为了获得一个整数(好吧,也许两个).这对于一个小型的个人项目来说觉得有点过分.

灵感来自StackOverflow讨论之一,有人建议T4模板可以完成这项工作.他们当然可以.该解决方案需要最少的工作量,并且不需要Visual Studio或构建流程定制.这里应该做些什么:

  1. 创建一个扩展名为".tt"的文件,并放置T4模板,生成AssemblyVersion和AssemblyFileVersion属性:
<#@ template language="C#" #>
// 
// This code was generated by a tool. Any changes made manually will be lost
// the next time this code is regenerated.
// 

using System.Reflection;

[assembly: AssemblyVersion("1.0.1.<#= this.RevisionNumber #>")]
[assembly: AssemblyFileVersion("1.0.1.<#= this.RevisionNumber #>")]
<#+
    int RevisionNumber = (int)(DateTime.UtcNow - new DateTime(2010,1,1)).TotalDays;
#>
Run Code Online (Sandbox Code Playgroud)

您将不得不决定版本号生成算法.对我来说,自动生成一个设置为自2010年1月1日以来的天数的修订号就足够了.正如您所看到的,版本生成规则是用简单的C#编写的,因此您可以根据需要轻松调整它.

  1. 上面的文件应该放在其中一个项目中.我用这个单独的文件创建了一个新项目,以使版本管理技术变得清晰.当我构建这个项目时(实际上我甚至不需要构建它:保存文件足以触发Visual Studio操作),生成以下C#:
// 
// This code was generated by a tool. Any changes made manually will be lost
// the next time this code is regenerated.
// 

using System.Reflection;

[assembly: AssemblyVersion("1.0.1.113")]
[assembly: AssemblyFileVersion("1.0.1.113")]
Run Code Online (Sandbox Code Playgroud)

是的,今天是自2010年1月1日起的113天.明天修订号将会改变.

  1. 下一步是从应共享相同自动生成的版本信息的所有项目中的AssemblyInfo.cs文件中删除AssemblyVersion和AssemblyFileVersion属性.而是为每个项目选择"添加现有项目",导航到包含T4模板文件的文件夹,选择相应的".cs"文件并将其添加为链接.那会的!

我喜欢这种方法的是它是轻量级的(没有自定义的MsBuild任务),并且自动生成的版本信息没有添加到源代码控制中.当然,使用C#版本生成算法可以打开任何复杂的算法.

  • 这对于为JS和CSS引用生成特定于构建的缓存清除令牌也很有用. (11认同)
  • 此外,仅在模板更改时才呈现这些模板.这仅适用于AutoT4 Visual Studio Extension或类似的东西. (4认同)
  • 我认为这是一个很好的解决方案,因为它具有附加组件和自定义可执行文件的灵活性,但它是一个纯粹的开箱即用的Visual Studio解决方案. (2认同)
  • 我不明白这个解决方案......我们必须调用TransformText()方法来获取结果文件... (2认同)

Dre*_*pin 42

这是我对T4建议的实现...这将在每次构建项目时增加构建号,而不管所选的配置(即Debug | Release),并且每次执行Release构建时它都会增加修订号.您可以通过应用程序➤装配信息继续更新主要和次要版本号...

为了更详细地解释,这将读取现有AssemblyInfo.cs文件,并使用正则表达式查找AssemblyVersion信息,然后根据输入增加修订和构建数字TextTransform.exe.

  1. 删除现有AssemblyInfo.cs文件.
  2. AssemblyInfo.tt在其位置创建一个文件.AssemblyInfo.cs保存T4文件后,Visual Studio应使用T4文件创建并对其进行分组.

    <#@ template debug="true" hostspecific="true" language="C#" #>
    <#@ output extension=".cs" #>
    <#@ import namespace="System.IO" #>
    <#@ import namespace="System.Text.RegularExpressions" #>
    <#
        string output = File.ReadAllText(this.Host.ResolvePath("AssemblyInfo.cs"));
        Regex pattern = new Regex("AssemblyVersion\\(\"(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<revision>\\d+)\\.(?<build>\\d+)\"\\)");
        MatchCollection matches = pattern.Matches(output);
        if( matches.Count == 1 )
        {
            major = Convert.ToInt32(matches[0].Groups["major"].Value);
            minor = Convert.ToInt32(matches[0].Groups["minor"].Value);
            build = Convert.ToInt32(matches[0].Groups["build"].Value) + 1;
            revision = Convert.ToInt32(matches[0].Groups["revision"].Value);
            if( this.Host.ResolveParameterValue("-","-","BuildConfiguration") == "Release" )
                revision++;
        }
    #>
    
    using System.Reflection;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;
    using System.Resources;
    
    // General Information
    [assembly: AssemblyTitle("Insert title here")]
    [assembly: AssemblyDescription("Insert description here")]
    [assembly: AssemblyConfiguration("")]
    [assembly: AssemblyCompany("Insert company here")]
    [assembly: AssemblyProduct("Insert product here")]
    [assembly: AssemblyCopyright("Insert copyright here")]
    [assembly: AssemblyTrademark("Insert trademark here")]
    [assembly: AssemblyCulture("")]
    
    // Version informationr(
    [assembly: AssemblyVersion("<#= this.major #>.<#= this.minor #>.<#= this.revision #>.<#= this.build #>")]
    [assembly: AssemblyFileVersion("<#= this.major #>.<#= this.minor #>.<#= this.revision #>.<#= this.build #>")]
    [assembly: NeutralResourcesLanguageAttribute( "en-US" )]
    
    <#+
        int major = 1;
        int minor = 0;
        int revision = 0;
        int build = 0;
    #>
    
    Run Code Online (Sandbox Code Playgroud)
  3. 将此添加到您的预构建事件:

    "%CommonProgramFiles(x86)%\microsoft shared\TextTemplating\$(VisualStudioVersion)\TextTransform.exe" -a !!BuildConfiguration!$(Configuration) "$(ProjectDir)Properties\AssemblyInfo.tt"
    
    Run Code Online (Sandbox Code Playgroud)

  • 可以使用 `"$(DevEnvDir)TextTransform.exe"` 代替 `"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\TextTransform.exe"` (4认同)
  • 您可以将当前的Visual Studio版本作为变量引用而不是"10":`"%CommonProgramFiles(x86)%\ microsoft shared\TextTemplating\$(VisualStudioVersion)\ TextTransform.exe"-a !! build!true"$( PROJECTDIR)属性\ AssemblyInfo.tt"` (3认同)
  • 预建活动非常重要!谢谢!一旦我这样做,每次我从VS2017或通过控制台构建项目时,我的版本号都会更新. (3认同)
  • @BurnsBA,感谢您的建议!我改变了答案来反映这一点. (2认同)
  • 对于 2017 社区版,我已将预构建事件更改为:``"c:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\TextTransform.exe" "$(ProjectDir)Properties \AssemblyInfo.tt"`` 对我来说效果很好。问候 (2认同)
  • 这是一个很好的解决方案,但是您的构建和修订位置混乱了。VS 特别指出:Major.Minor.Build.Revision,因此您必须在模板中交换 Revision 和 Build。1. new Regex("AssemblyVersion\\(\"(?&lt;major&gt;\\d+)\\.(?&lt;minor&gt;\\d+)\\.(?&lt;build&gt;\\d+)\\.(? &lt;修订版&gt;\\d+)\"\\)"); 2. [程序集:AssemblyVersion("&lt;#= this.major #&gt;.&lt;#= this.minor #&gt;.&lt;#= this.build #&gt;.&lt;#= this.revision #&gt;")] 3. [程序集: AssemblyFileVersion("&lt;#= this.major #&gt;.&lt;#= this.minor #&gt;.&lt;#= this.build #&gt;.&lt;#= this.revision #&gt;")] (2认同)

gid*_*eon 31

如果您在构建和修订中添加星号,则visual studio将使用自2000年1月1日以来的天数作为内部版本号,并将自午夜以来的秒数除以2作为修订版.

更好的救生解决方案是http://autobuildversion.codeplex.com/

它就像一个魅力,它非常灵活.


gal*_*ets 23

以下是来自MSDN的AssemblyInfo.cs引用:

您可以指定所有值,也可以使用星号()接受默认的内部版本号,修订号或两者.例如,[assembly:AssemblyVersion("2.3.25.1")]表示2为主要版本,3表示次要版本,25表示构建号,1表示版本号.诸如[assembly:AssemblyVersion("1.2. ")]之类的版本号指定1作为主要版本,2指定为次要版本,并接受默认的构建和修订号.诸如[assembly:AssemblyVersion("1.2.15.*")]之类的版本号指定1作为主要版本,2作为次要版本,15作为构建号,并接受默认修订号.默认内部版本号每天递增.默认修订号是随机的

这有效地说,如果你将1.1.*放入汇编信息中,只有内部编号会自动增加,并且不会在每次构建之后发生,而是每天发生.修订号将改变每个构建,但是随机,而不是以递增的方式.

对于大多数用例来说,这可能就足够了.如果这不是您正在寻找的东西,那么您将不得不编写一个脚本,该脚本将在预构建步骤中自动增加版本#

  • 它随机增加?他们开玩笑吧? (28认同)
  • 根据http://msdn.microsoft.com/en-us/library/system.reflection.assemblyversionattribute.aspx上留下的评论,修订号不是随机的,而是它是自12 AM以来的秒数除以2 ,在我看来并不是那么糟糕. (25认同)

Mic*_*ith 15

使用AssemblyInfo.cs

在App_Code中创建文件:并填写以下内容或使用Google获取其他属性/属性的可能性.

AssemblyInfo.cs中

using System.Reflection;

[assembly: AssemblyDescription("Very useful stuff here.")]
[assembly: AssemblyCompany("companyname")]
[assembly: AssemblyCopyright("Copyright © me 2009")]
[assembly: AssemblyProduct("NeatProduct")]
[assembly: AssemblyVersion("1.1.*")]
Run Code Online (Sandbox Code Playgroud)

AssemblyVersion是你真正追求的部分.

然后,如果您正在使用网站,任何aspx页面或控件,您可以添加<Page>标记,如下所示:

CompilerOptions="<folderpath>\App_Code\AssemblyInfo.cs"
Run Code Online (Sandbox Code Playgroud)

(当然,用适当的变量替换folderpath).

我不认为你需要以任何方式为其他类添加编译器选项; App_Code中的所有内容在编译时都应该收到版本信息.

希望有所帮助.


Mun*_*Mun 10

您可以尝试使用Matt Griffith的UpdateVersion.它现在已经很老了,但效果很好.要使用它,您只需要设置一个指向AssemblyInfo.cs文件的预构建事件,应用程序将根据命令行参数相应地更新版本号.

由于应用程序是开源的,我还创建了一个版本,使用格式(主要版本)增加版本号.(次要版本).([year] [dayofyear]).(增量).有关此内容和修订代码的更多信息,请参阅我的博客条目,汇编版本号和.NET.

更新:我已将修改后的UpdateVersion应用程序版本的代码放在GitHub上:https://github.com/munr/UpdateVersion

  • http://code.mattgriffith.net/UpdateVersion/因403.6错误代码而失败 (3认同)

Dmi*_*7ry 10

  • 版本中的明星(如"2.10.3.*") - 很简单,但数字太大了

  • AutoBuildVersion - 看起来很棒,但它不适用于我的VS2010.

  • @DrewChapin的脚本有效,但我不能在我的工作室中为Debug预构建事件和Release预构建事件设置不同的模式.

所以我改变了脚本... commamd:

"%CommonProgramFiles(x86)%\microsoft shared\TextTemplating\10.0\TextTransform.exe" -a !!$(ConfigurationName)!1 "$(ProjectDir)Properties\AssemblyInfo.tt"
Run Code Online (Sandbox Code Playgroud)

和脚本(这适用于"调试"和"发布"配置):

<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Windows.Forms" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#
    int incRevision = 1;
    int incBuild = 1;

    try { incRevision = Convert.ToInt32(this.Host.ResolveParameterValue("","","Debug"));} catch( Exception ) { incBuild=0; }
    try { incBuild = Convert.ToInt32(this.Host.ResolveParameterValue("","","Release")); } catch( Exception ) { incRevision=0; }
    try {
        string currentDirectory = Path.GetDirectoryName(Host.TemplateFile);
        string assemblyInfo = File.ReadAllText(Path.Combine(currentDirectory,"AssemblyInfo.cs"));
        Regex pattern = new Regex("AssemblyVersion\\(\"\\d+\\.\\d+\\.(?<revision>\\d+)\\.(?<build>\\d+)\"\\)");
        MatchCollection matches = pattern.Matches(assemblyInfo);
        revision = Convert.ToInt32(matches[0].Groups["revision"].Value) + incRevision;
        build = Convert.ToInt32(matches[0].Groups["build"].Value) + incBuild;
    }
    catch( Exception ) { }
#>
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Game engine. Keys: F2 (Debug trace), F4 (Fullscreen), Shift+Arrows (Move view). ")]
[assembly: AssemblyProduct("Game engine")]
[assembly: AssemblyDescription("My engine for game")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyCopyright("Copyright © Name 2013")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components.  If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type. Only Windows
// assemblies support COM.
[assembly: ComVisible(false)]

// On Windows, the following GUID is for the ID of the typelib if this
// project is exposed to COM. On other platforms, it unique identifies the
// title storage container when deploying this assembly to the device.
[assembly: Guid("00000000-0000-0000-0000-000000000000")]

// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version
//      Build Number
//      Revision
//
[assembly: AssemblyVersion("0.1.<#= this.revision #>.<#= this.build #>")]
[assembly: AssemblyFileVersion("0.1.<#= this.revision #>.<#= this.build #>")]

<#+
    int revision = 0;
    int build = 0;
#>
Run Code Online (Sandbox Code Playgroud)