Auto-increment build numbers in Visual Studio

You need to auto-increment your build numbers in order to easily tell which code you’re working with.

In this article I’ll explain how to auto-increment your build numbers in Visual Studio. I’ll be using text templating to generate the Assembly Version.

Update (2021-3-27): I added information about how to handle this .NET Core projects.

1 – Choose a versioning scheme

I’m going to be using the version scheme: <Major Version>.<Minor Version>.<Days Since Project Started>.<Minutes Since Midnight>. You should use whatever makes sense for you.

The one rule you must adhere to is the numbers must be <= 65534 (because they are 16-bit). If you generate a number greater than 65534, you’ll get a build error:

CS7034 The specified version string does not conform to the required format - major[.minor[.build[.revision]]]
Code language: plaintext (plaintext)

There are 86400 seconds per day and 1440 minutes per day. This is why I chose Minutes Since Midnight instead of Seconds Since Midnight. Since 86400 > the limit of 65534, using seconds would sometimes result in the build error shown above. By using minutes, this cannot happen.

2 – Comment out the assembly version properties

Open AssemblyInfo.cs and comment out AssemblyVersion and AssemblyFileVersion.

Note: This step doesn’t apply to .NET Core projects, unless you added an AssemblyInfo.cs file manually.

3 – Add a Text Template file

A Text Template is used to generate code. We’ll be using this to generate the assembly version.

After you add this file you’ll get a warning prompt. Since you’re the one adding this file, you can check the box and click OK.

4 – Update the Text Template to generate the AssemblyVersion property

There’s two parts to the text template:

  1. Specifying the template using placeholder variables.
  2. Populating the placeholder variables.
<#@ template debug="false" hostspecific="false" language="C#" #> <#@ output extension=".cs" #> using System.Reflection; [assembly: AssemblyVersion("<#= this.Major #>.<#= this.Minor #>.<#= this.DaysSinceProjectStarted #>.<#= this.MinutesSinceMidnight #>")] <#+ int Major = 1; int Minor = 0; static DateTime ProjectStartedDate = new DateTime(year: 2020, month: 3, day: 12); int DaysSinceProjectStarted = (int)((DateTime.UtcNow - ProjectStartedDate).TotalDays); int MinutesSinceMidnight = (int)DateTime.UtcNow.TimeOfDay.TotalMinutes; #>
Code language: C# (cs)

5 – Update .csproj to run the text template every time it builds

You have to add a few a few properties to the .csproj file to make it run the transform every time it builds. Check the appropriate section below based on if you’re using .NET Framework or .NET Core.

Updating the .csproj in a .NET Framework project

  1. Edit your .csproj in Notepad.
  2. Make two changes:
    • Import Microsoft.TextTempatings.targets.
    • Add the TransformOnBuild, OverwriteReadOnlyOutputFiles, and TransformOutOfDateOnly properties to each build configuration.

The .csproj below shows these highlighted changes:

<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Import Project="$(MSBuildExtensionsPath)$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <ProjectGuid>{221097A0-A3F4-45CC-A6C0-B13455C6EAFE}</ProjectGuid> <OutputType>Library</OutputType> <AppDesignerFolder>Properties</AppDesignerFolder> <RootNamespace>AutoIncrementingBuild</RootNamespace> <AssemblyName>AutoIncrementingBuild</AssemblyName> <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> <FileAlignment>512</FileAlignment> <Deterministic>true</Deterministic> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> <OutputPath>bin\Debug\</OutputPath> <DefineConstants>DEBUG;TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <TransformOnBuild>true</TransformOnBuild> <OverwriteReadOnlyOutputFiles>true</OverwriteReadOnlyOutputFiles> <TransformOutOfDateOnly>false</TransformOutOfDateOnly> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <DebugType>pdbonly</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release\</OutputPath> <DefineConstants>TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <TransformOnBuild>true</TransformOnBuild> <OverwriteReadOnlyOutputFiles>true</OverwriteReadOnlyOutputFiles> <TransformOutOfDateOnly>false</TransformOutOfDateOnly> </PropertyGroup> <ItemGroup> <Reference Include="System" /> <Reference Include="System.Core" /> <Reference Include="System.Xml.Linq" /> <Reference Include="System.Data.DataSetExtensions" /> <Reference Include="Microsoft.CSharp" /> <Reference Include="System.Data" /> <Reference Include="System.Net.Http" /> <Reference Include="System.Xml" /> </ItemGroup> <ItemGroup> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="VersionAutoIncrement.cs"> <AutoGen>True</AutoGen> <DesignTime>True</DesignTime> <DependentUpon>VersionAutoIncrement.tt</DependentUpon> </Compile> </ItemGroup> <ItemGroup> <Content Include="VersionAutoIncrement.tt"> <Generator>TextTemplatingFileGenerator</Generator> <LastGenOutput>VersionAutoIncrement.cs</LastGenOutput> </Content> </ItemGroup> <ItemGroup> <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" /> </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v16.0\TextTemplating\Microsoft.TextTemplating.targets" /> </Project>
Code language: HTML, XML (xml)

Note: This is specifically adding the VS2019 path.

Updating the .csproj in a .NET Core project

  1. Click on the project so it opens the .csproj file for editing.
  2. Add the following properties
    1. Import Microsoft.TextTempatings.targets.
    2. Disable GenerateAssemblyInfo.
    3. Add the TransformOnBuild, OverwriteReadOnlyOutputFiles, and TransformOutOfDateOnly.
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netcoreapp3.1</TargetFramework> </PropertyGroup> <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v16.0\TextTemplating\Microsoft.TextTemplating.targets" /> <PropertyGroup> <GenerateAssemblyInfo>false</GenerateAssemblyInfo> <TransformOnBuild>true</TransformOnBuild> <OverwriteReadOnlyOutputFiles>true</OverwriteReadOnlyOutputFiles> <TransformOutOfDateOnly>false</TransformOutOfDateOnly> </PropertyGroup> <ItemGroup> <None Include="VersionAutoIncrement.cs"> <DesignTime>True</DesignTime> <AutoGen>True</AutoGen> <DependentUpon>VersionAutoIncrement.tt</DependentUpon> </None> </ItemGroup> <ItemGroup> <None Update="VersionAutoIncrement.tt"> <Generator>TextTemplatingFileGenerator</Generator> <LastGenOutput>VersionAutoIncrement.cs</LastGenOutput> </None> </ItemGroup> <ItemGroup> <Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" /> </ItemGroup> </Project>
Code language: HTML, XML (xml)

If you don’t disable GenerateAssemblyInfo, then you’ll get a compile time error saying there’s a duplicate AssemblyVersion.

Note: This is specifically adding the VS2019 path.

6 – Build the project

When you build the project it’ll execute the text template. This generates a source file with the AssemblyVersion property.

22 thoughts on “Auto-increment build numbers in Visual Studio”

  1. To me, this is totally not working!
    Yes, the VersionAutoIncrementer.cs file is created, but it is stuck in its original version number.
    E.g. stuck in:

    using System.Reflection;
    [assembly: AssemblyVersion(“8.0.220.927”)]

    Reply
    • Hi Bjorn,

      Good catch. I updated the page to include instructions for how to update your .csproj file to make it run the text templating transform on every build.

      Please take a look at the step I added to the page – 5 – Update .csproj to run the text template every time it builds.

      Let me know if this helps.

      Reply
  2. I have created the template for shared assembly info.tt its updating version number only once for the first build or rebuild.
    If I change some code and try to build the whole project it’s not reflecting the version number.

    Reply
      • Hi Mak,
        Yes, I did. if I do changes related to that project then the revision number getting updated.if anything other than that project it’s not updating.

        Below is my code in SharedAssembyInfo.tt

        using System.Reflection;

        [assembly: AssemblyConfiguration(“”)]
        [assembly: AssemblyCompany(“GRIDSMART Technologies, Inc.”)]
        [assembly: AssemblyCopyright(“Copyright © GTI 2019”)]
        [assembly: AssemblyTrademark(“”)]
        [assembly: AssemblyCulture(“”)]
        [assembly: AssemblyVersion(“…”)]

        And followed all the steps included the below line in .csproj

        Reply
        • The text template only runs if the project is built. If there’s no changes in the project, then clicking “Build” in Visual Studio won’t build that project. Therefore it won’t run the text template.

          I’m not aware of a way to make “Build” build all projects, even if there are no changes.

          However, since it sounds like you want to run the text template every time, regardless of changes to the project, it really sounds like you want to use “Rebuild” instead of “Build” every time. That’s the simplest solution. There may be other ways to force VS to build your unchanged project, but “Rebuild” definitely makes it do that.

          Reply
    • Hi Chris,

      That’s unusual that the Text Template is missing. What version of Visual Studio are you using? Which .NET version? and which language (C# or VB)?

      Do you have the Text Template Transformation component installed?

      Update (2021-3-27): I just ran into this when using a specific project type. You can just add the Text File, change the extension to .tt, and paste in the text templating code (use the code shown in this article as a starting point). VS updates the .csproj the same way as if you had added a Text Template new item. I think this is a bug in VS, where it’s not showing the Text Template item as an option, even though it’s installed.

      Reply
    • Hi Sid,

      Thanks for your question. I realized I didn’t have a section showing how to update a .NET Core .csproj. So I just added this section: Updating the .csproj in a .NET Core project

      Regarding your question about Blazor .NET Core specifically, the instructions in the article (including the new .NET Core section) work fine. I just tried it out by creating a new Blazor app targeting .NET Core. I added the VersionAutoIncrement.tt and .csproj updates to all three projects (Shared/Client/Server). Then rebuilt the solution and it ran all the transforms and updated all of their assembly versions. Note: I haven’t used Blazor before, so I’m not sure if updating the version in all three projects would matter.

      A few things:
      1. When I tried to add a new Text Template in the Client/Server projects, “Text Template” was missing from this list. I just picked a Text File and changed the extension to .tt and put the text templating code (from this article) and it was fine. It must be a bug in VS causing it to not show Text Template in this list of new items.

      2. The text template runs for each project separately. In this article, it shows how to put the TotalMinutes as part of the version. This means if you are building multiple projects, it’s possible for the final assembly versions to be different between different assemblies built together.

      Please try it out and let me know how it goes.

      Reply
  3. Hi Mak,

    Thanks. This is exactly what i was looking for ! It works well with .Net 5 (windows form app)
    I have replaced the date with an increment counter at each build
    So I manually manage major, minor, revision and build is automatically increase by 1

    Bye

    Reply
  4. Is there a way to reference the generated version number in an HTML page (i.e. layout page) to display the version number? Since I’ve never worked with templates or generated files, I can’t figure out the proper way to reference the generated cs file to access the assembly parameter.

    Reply
    • Hi Chris,

      The generated assembly version can be accessed programmatically at runtime by calling System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;

      What type of project are you referring to?

      I just created an ASP.NET MVC project (with the default controllers and views) and got it to display the generated assembly version by doing this:
      1. In Controllers > HomeController.Index() I put ViewBag.Version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
      2. In Views > Home > Index.cshtml, I put <p>@ViewBag.Version</p>
      3. Launch the homepage. The version appears at the bottom.

      I hope this helps. If not, please let me know what type of project you are doing exactly.

      Reply
      • It is a .NET MVC project. It’s just passing 0.0.0.0 for me. The actual version in the generated .cs file is working as intended, but it’s not being referenced properly for some reason. AssemblyInfo lines are commented out. Template is created and version is being generated on build. Is there a reference to the generated file that I’m not making somewhere?

        I’m referencing it directly in the Layout vs. sending it from the controller:

        Version: @System.Reflection.Assembly.GetExecutingAssembly().GetName().Version

        is returning

        Version: 0.0.0.0

        But in the generated version file I see: [assembly: AssemblyVersion(“1.0.693.1116”)]

        Reply
        • Try using GetEntryAssembly() instead of GetExecutingAssembly(), like this:
          <p>@System.Reflection.Assembly.GetEntryAssembly().GetName().Version</p>

          When you call @System.Reflection.Assembly.GetExecutingAssembly().GetName().Version directly in the view (instead of populating ViewBag.Version from the controller), the executing assembly is YourAssembly.Views.dll instead of YourAssembly.dll. This is why it’s showing version 0.0.0.0 (the version of YourAssembly.Views.dll) instead of 1.0.693.1116 (the version of YourAssembly.dll).

          Reply
          • This got me closer, but causes a null reference error due to an issue between managed/unmanaged code. It did lead me on the correct path to locate a solution. To get it working directly in the layout page I used:

            typeof([application-namespace].MvcApplication).Assembly.GetName().Version

            This is great work and thanks for all your help in getting this rolling.

          • I’m glad you were able to solve it! Thanks for sharing what worked for you – I’m sure it’ll help others who run into the same scenario.

  5. Hello,
    What is the VB version of this code.
    typeof([application-namespace].MvcApplication).Assembly.GetName().Version

    I get the NullReferenceException as well for a web application in visual studio 2019 in visual basic.

    I appreciate all the work you guys done and thank you for your help.

    Reply
    • Hi Hasan,

      I got this working in an ASP.NET Web App using VB.NET with the following:

      @(GetType(YourProjectNamespace.MvcApplication).Assembly.GetName().Version)

      Reply
  6. Hi Mak
    Thank you for the great instructions, it worked exactly as you said. Do you know how I could reference and use the version created during the build in Wix toolset’s .wxs file?

    Reply
    • Let’s say you’re auto-generating build numbers for a file called ASPNETVersion.dll. And you’re including this file in the WSX file with the following definition:

      <File Id=”MyDLL” Name=”ASPNETVersion.dll” Source=”C:\Projects\ASPNETVersion.dll” />

      To set the installer version to this DLL’s file version, do this:

      <Product Id=”*” Name=”Wix” Language=”1033″ Version=”!(bind.fileVersion.MyDLL)” Manufacturer=”Test” UpgradeCode=”23efaa51-e3b8-46d6-85bb-888f84123ef6″>

      Reply

Leave a Comment