none
How to have an auto incrementing version number in Visual Studio RRS feed

  • Question

  • I currently hard code a version number in a site master page using a label but I want to be able to displayed the version number by auto increment it instead of a hard code text. How do I do that? 

    Here is the current code for the label

    <asp:Label runat="server" ID="lblBuildNumber" ForeColor="White" Text="Build: 1.3.027" />

    I have the version number in a AssemblyInfo.cs file.

    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("Miller.BL")]
    [assembly: AssemblyDescription("")]
    [assembly: AssemblyConfiguration("")]
    [assembly: AssemblyCompany("Microsoft")]
    [assembly: AssemblyProduct("Miller.BL")]
    [assembly: AssemblyCopyright("Copyright © Microsoft 2009")]
    [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.
    [assembly: ComVisible(false)]
    
    // The following GUID is for the ID of the typelib if this project is exposed to COM
    [assembly: Guid("ea9aeb9b-08d8-42e1-b17d-e0e9cd498872")]
    
    // Version information for an assembly consists of the following four values:
    //
    //      Major Version
    //      Minor Version 
    //      Build Number
    //      Revision
    //
    // You can specify all the values or you can default the Build and Revision Numbers 
    // by using the '*' as shown below:
    // [assembly: AssemblyVersion("1.0.*")]
    [assembly: AssemblyVersion("1.0.0.0")]
    [assembly: AssemblyFileVersion("1.0.0.0")]
    

    Thursday, September 27, 2018 8:44 PM

All replies

  • See the following https://github.com/BalassaMarton/MSBump

    Please remember to mark the replies as answers if they help and unmark them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.
    VB Forums - moderator
    profile for Karen Payne on Stack Exchange, a network of free, community-driven Q&A sites

    Thursday, September 27, 2018 9:33 PM
    Moderator
  • As per the instructions in the assembly info file (use a wildcard), 

    // You can specify all the values or you can default the Build and Revision Numbers 
    // by using the '*' as shown below:
    // [assembly: AssemblyVersion("1.0.*")]

    If you want to display this in the title of your application use: 

    Application.ProductVersion



    Friday, September 28, 2018 9:02 AM
  • Another angle is that the current version number of 1.3.027 is hardcoded on the website so how do I increment from this number or at least start at 1.4.0 going forward.

    Right now I'm using a label and setting the label to version number in the Page Load

     <div class="row">
            <div class="col-md-12" style="background-color:gray;">
                <asp:Label runat="server" ID="lblBuildNumber" ForeColor="White" Text="" />
            </div>
        </div>
    protected void Page_Load(object sender, EventArgs e)
    {
        Version version = Assembly.GetExecutingAssembly().GetName().Version;
        lblBuildNumber.Text = version.ToString();
    }


    • Edited by bootzilla Wednesday, October 10, 2018 4:06 PM
    Wednesday, October 10, 2018 2:46 PM
  • Set your assembly info to 1.4.*

    Visual studio will create an incremental build number each time you build.

    There are other ways of achieving versioning for the front end, depending on the complexity of your setup and how you want to version your site (version numbers [as above], build date [more meaningful imo], git commit [useful for code tracking], a combination, etc.)

    I quite like to use "build date + git commit" as it tells more than just an arbitrary number.

    The more complex examples can be setup by tokenizing the config file and then swapping in the values in your CI/CD pipeline (presumably can also be done during a "publish" from Visual Studio).

    Monday, October 15, 2018 8:18 AM
  • Set your assembly info to 1.4.*

    Visual studio will create an incremental build number each time you build.

    There are other ways of achieving versioning for the front end, depending on the complexity of your setup and how you want to version your site (version numbers [as above], build date [more meaningful imo], git commit [useful for code tracking], a combination, etc.)

    I quite like to use "build date + git commit" as it tells more than just an arbitrary number.

    The more complex examples can be setup by tokenizing the config file and then swapping in the values in your CI/CD pipeline (presumably can also be done during a "publish" from Visual Studio).

    Yes I do want to create versioning using the front end that starts with the 1.4.* and go from there. I want to auto increment that number every time I publish a project. How do I do that on the front end? The version number I want to use is 1.4.001 and increment from that. The 001 represents the 1 iteration of changes since the 4th major release. Does this make since.
    Monday, October 15, 2018 6:59 PM
  • The default behaviour of visual studio (when using the asterisk in the assembly info) is to increment with each build - a publish will cause a build so this will work. However i'm guessing you will do other builds between publishing (while debugging) so each published version of site will jump by more than just 1 version number.

    If that's fine, then go ahead and use the asterisk in your assembly info.

    If you only want to increment a version number when you specifically publish the site then that won't work. You can look at other ways of achieving this (custom publish steps, azure devops steps, octopus steps) but they will probably be based on some other value outside of the assembly info file. 

    Tuesday, October 16, 2018 8:24 AM
  • I have this in my page load:

    Version version = Assembly.GetExecutingAssembly().GetName().Version;
                lblBuildNumber.Text = version.ToString();


    his in my html:

     <asp:Label runat="server" ID="Label1" ForeColor="White" Text="Build:" />&nbsp;
                                    <asp:Label runat="server" ID="lblBuildNumber" ForeColor="White" Text="" Visible="true" />

    and this in the assembly.cs

    [assembly: AssemblyVersion("1.4.*")]

    and I get this 1.4.6865.30445. Not sure where it gets that number from but would prefer it to be 1.4.0 and then increment from there. How would I do that?

    Thursday, October 18, 2018 8:58 PM
  • Read here on how it works

    "

    The version number has four parts, as follows:

    <major version>.<minor version>.<build number>.<revision>

    You can specify all the values or you can accept the default build number, revision number, or both by using an asterisk (*). For example, [assembly:AssemblyVersion("2.3.25.1")] indicates 2 as the major version, 3 as the minor version, 25 as the build number, and 1 as the revision number. A version number such as [assembly:AssemblyVersion("1.2.*")] specifies 1 as the major version, 2 as the minor version, and accepts the default build and revision numbers. A version number such as [assembly:AssemblyVersion("1.2.15.*")] specifies 1 as the major version, 2 as the minor version, 15 as the build number, and accepts the default revision number. The default build number increments daily. The default revision number is the number of seconds since midnight local time (without taking into account time zone adjustments for daylight saving time), divided by 2."

    If that doesn't work for what you need, then you'll have to use a custom solution.

    Friday, October 19, 2018 3:28 PM
  • So if the Assembly Version is [1.2.15] does that next build go to 1.2.16 and so on?

    I did try it for the AssemblyFileVersion and that seems like that will work, right? I put this on the Assembly.cs file:

    [assembly: AssemblyVersion("1.4.*")]
    [assembly: AssemblyFileVersion("1.4.001")]

    This is what I have for the C# code

      Assembly assembly = Assembly.GetExecutingAssembly();
                FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
                string version = fvi.FileVersion;
                //Version version = Assembly.GetExecutingAssembly().GetName().Version;
                lblBuildNumber.Text = version.ToString();


    • Edited by bootzilla Monday, October 22, 2018 6:38 PM
    Monday, October 22, 2018 2:16 PM
  • Anyone with any ideas. I'm going to need help with a custom build as craig suggested but not sure how to do that.
    Tuesday, October 23, 2018 2:42 PM
  • The wildcard support is limited to incrementing the # following the standard Windows rules. Windows requires a 4 digit version so you cannot use it for semantic versioning (1.4.0). If you want semantic versioning then you'll have to "truncate" the last version number. You can do this easily for display purposes by getting the assembly version and then removing the last digit during the conversion to a string.

    The version is also going to be non-padded. If you want padding then you have to do it yourself. Personally I think you're trying to put too much customization into an assembly version # and that isn't where it belongs.

    Versioning is a build-time setting so you should handle this in your build system (if you have one). All of them support various approaches to versioning and most should be able to auto-update your AssemblyInfo files. If you need an example of how you might do this then I wrote up an example of how we used TFS to auto-version our builds.

    However the landscape has changed and VS 2017 supports doing some build-time stuff even in VS using a custom directory.build.targets file. You can also do versioning there. We do that for our stuff now and it versions whenever anyone builds (although we turn it off for local builds). More importantly you can put your version information into a directory.build.props (or perhaps version.props) file and MSbuild will extract your version information directly from there. To auto-increment the # you'll need to decide the rules to use. You cannot simply look at the last version because it is probably already gone. Most people tend to use a time of day or something (in build systems they tend to provide a revision number). If you want something more advanced than what the wildcard support in AssemblyVersion handles then you'll need to go this route. But honestly I'd just stick with AssemblyVersion wildcards unless you really, really need customized versioning.


    Michael Taylor http://www.michaeltaylorp3.net

    Tuesday, October 23, 2018 5:56 PM
    Moderator
  • I actually am in the process of coming up with another way to do this in a custom build. I added this console app to global.asax that reads the file, increments the build and saves the file:

     protected void Application_Start(object sender, EventArgs e)
            {
              
                Version version;
                // deserialize JSON directly from a file
                using (StreamReader file = File.OpenText(Server.MapPath("Version.json")))
                {
                    JsonSerializer serializer = new JsonSerializer();
                    version = (Version)serializer.Deserialize(file, typeof(Version));
                }
    
                Application["Version"] = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build.ToString().PadLeft(3, '0'));
            }

    I create a version type json file with this in it:

    {
      "Major": 1,
      "Minor": 4,
      "Build": 1,
      "Revision": 0,
      "MajorRevision": 0,
      "MinorRevision": 0
    }
    

    and then that displays the build number on the html page:

    <span style="color:white"><%=Application["Version"] %></span>--%>

    What I want to do is invoke the console app in a post build event and the console app writes the file to the application root. This is where I need assistance because I'm not sure what to specifically put in the build event. I did some research and do I put in like an xcopy type of thing for this? If someone can assist me with this I would greatly appreciate it.

    Wednesday, October 24, 2018 2:35 PM
  • I really don't recommend you go that route. The app version is determined by the binary file, not some config file at runtime. There are many, many tools available to auto-increment your versioning at build time. This is when it needs to happen.

    You haven't specified how you build your "official" version so it is hard to say. I would personally say use a directory.build.props file that has the version information in it. At build time increment the patch # by 1. This is easily done using Powershell which can be triggered by a directory.build.targets file. Then your assembly(ies) have the correct version. It then becomes a simple matter in your app to get the version from the assembly. This is how our apps get their version info for our users and it works very well.


    Michael Taylor http://www.michaeltaylorp3.net

    Wednesday, October 24, 2018 3:01 PM
    Moderator
  • I really don't recommend you go that route. The app version is determined by the binary file, not some config file at runtime. There are many, many tools available to auto-increment your versioning at build time. This is when it needs to happen.

    You haven't specified how you build your "official" version so it is hard to say. I would personally say use a directory.build.props file that has the version information in it. At build time increment the patch # by 1. This is easily done using Powershell which can be triggered by a directory.build.targets file. Then your assembly(ies) have the correct version. It then becomes a simple matter in your app to get the version from the assembly. This is how our apps get their version info for our users and it works very well.


    Michael Taylor http://www.michaeltaylorp3.net

    Hi Michael,

    I thought that I had specifically stated how i wanted to build the official version. Maybe not. Anyway the version number should auto increment the build number portion of the assembly everytime me or someone else does a build on the project. It should start with version number 1.4.001 and then the next build will be 1.4.002 and so on. I want to display that version number as a counter of sorts on the login page of the website.

    I reviewed the directory.build.targets.file and it seems a little confusing to me. I haven't done something like that. Does that align with what I described in the paragraph above of what I'm trying to accomplish?

    Wednesday, October 24, 2018 4:02 PM
  • "everytime me or someone else does a build on the project."

    Are you sure that is the way you want to go? Are you using version control? If so then every time somebody does a build, irrelevant if they change anything then your assemblyinfo.cs file will be updated resulting in a checkout from your source control system or, at the very minimum, a change detection (for Git). Given that you'll likely be making changes and rebuilding your code over and over again as you fix compiler errors and logic errors then the version # will increment. Given that you are using a 3 digit value then you only get 1000 builds which doesn't sound like much until you consider how often you do builds throughout the day. Normally you would only want to do that for a "potentially releasable" build. Hence normally this is done via your build system. Although you could also configure it to only happen for Release builds in VS as well and that would alleviate the continual changes. 

    "1.4.001"

    I'm afraid you cannot do that. As I already mentioned, Windows requires version #s to be 4 parts and it'll ignore any zero prefixes. So your actual version # would be 1.4.1.0. There is nothing you can do to configure your assembly version (as seen by Windows and all other apps) to change that.

    The only string-based version you can control in this manner is AssemblyInformationalVersion. You can set your informational version to be that value and that is often how we do semantic versioning but the actual assembly/file version will not match. Even with semantic versioning, I believe, zero prefixes are ignored so your version # is 1.4.1. 

    Is there any particular reason you are really set on using this format? Even if you have to write hacks in your code to get the information from what is otherwise a Windows-defined version?

    Going back to the beginning, if you are OK accepting the fact that your version # must follow Windows convention then putting 1.4.* in your AssemblyInfo.cs file should be sufficient for it to generate versions as 1.4.1, 1.4.2 each time you build. This meets your requirement other than the zero prefix, I think.

    If you simply want a unique identifier for each build (the more common requirement) then consider using something else. In many builds we use the Julian date. Of course if you build more than once in a single day you'd get the same date. That is where build systems like AppVoyeor and TFS add revision information so normally we do something like 1.4.<juliandate>.<rev> where <rev> is incremented for each build.

    The fundamental problem you're trying to work around is that you have a base version like 1.4 and you want to auto-increment the patch #. But if you don't store that patch # somewhere then there is no way to know what the "last version" was. To store the patch # you have to update something that is checked into source control. 

    After all that, here's some starter code for working with directory.build.* to get you started down the path of generating a different version each time. But for this to work you have to update a file and ensure it is checked into your source control system after every build. This won't probably work as is but you can see the logic involved in getting versioning work based upon your requirements. This code is based upon code we use to auto-version our apps but it is experimental and is really designed to only be used for automated builds that we use.

    <!-- directory.build.props -->
    <Project>
        <!-- Solution level properties are set here -->
    	<PropertyGroup>
          <MajorVersion>1</MajorVersion>
          <MinorVersion>4</MinorVersion>
          <PatchVersion>0</PatchVersion>      	
    	  
    	  <!-- These are standard properties that project's have -->
    	  <Product>MyProduct</Product>
    	  <Company>MyCompany</Company>
    	  <Trademark></Trademark>
    	  
    	  <!-- As you switch to the newer SDK project format you don't have an AssemblyInfo file anymore so opt out as needed -->
    	  <UpdateAssemblyInfoFiles>true</UpdateAssemblyInfoFiles>
       </PropertyGroup>
    </Project>

    <!-- directory.build.targets --> 
    <?xml version="1.0" encoding="utf-8"?>
    <Project ToolsVersion="15.0" InitialTargets="SetVersion" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
        	  
       <Target Name="SetVersion">  
          <Error Condition="'$(MajorVersion)'==''" Text="Project property file doesn't specify version properties. Fix the property file before building again."/>
    
    	  <!-- These are some common properties that are either conditionally set by a build or passed on the command line -->      
    	  <PropertyGroup>
    	     <BuildLabel Condition="'$(BuildLabel)'==''"></BuildLabel>
    		 <IsPrerelease Condition="'$(IsPrerelease)'==''">false</IsPrerelease>
          </PropertyGroup>
    	  
    	  <!-- Normally the build version is the one that auto-increments and is often based upon the date.
    	  
    	       BuildVersion is normally provided by the automated build system. It is this value we generally change for each build.
    		 -->
          <PropertyGroup>
             <BuildVersion Condition="'$(BuildVersion)'==''">0</BuildVersion>
          </PropertyGroup>
    	  
          <!-- Store the version information into temporary variables       
    	  
    	       Product is semantic versioned - {major}.{minor}.{patch}
    		   Assembly has to be a full Windows version and should be valid for strong name assemblies - {major}.{minor}.{patch}.0 
    		   File is the actual version including the build # - {major}.{minor}.{patch}.{build}
    	    -->
          <PropertyGroup>
             <Temp_ProductVersion Condition="'$(Temp_ProductVersion)'==''">$(MajorVersion).$(MinorVersion).$(PatchVersion)</Temp_ProductVersion>
             <Temp_AssemblyVersion Condition="'$(Temp_AssemblyVersion)'==''">$(ProductVersion).0</Temp_AssemblyVersion>
             <Temp_FileVersion Condition="'$(Temp_FileVersion)'==''">$(ProductVersion).$(BuildVersion)</Temp_FileVersion>
          </PropertyGroup>
    
    	  <!-- For packages and other "versioning" that supports semantic versioning include any additional attributes that are commonly needed -->
          <PropertyGroup Condition="'$(SemanticVersion)'==''">
             <SemanticVersion>$(ProductVersion)</SemanticVersion>
    
             <SemanticVersion Condition="'$(BuildLabel)'!=''">$(SemanticVersion)-$(BuildLabel)</SemanticVersion>
             <SemanticVersion Condition="'$(IsPrerelease)'=='true' And '$(BuildVersion)'!='0'">$(SemanticVersion)-$(BuildVersion)</SemanticVersion>
          </PropertyGroup>
    
    	  <!-- This is the only string-based version that is supported and can technically be anything you want. -->
          <PropertyGroup Condition="'$(InformationalVersion)'==''">
             <InformationalVersion>$(SemanticVersion)</InformationalVersion>
          </PropertyGroup>
    	  
          <!-- Set final version information used by projects -->
          <PropertyGroup>
             <Version>$(SemanticVersion)</Version>
             <AssemblyVersion>$(AssemblyVersion)</AssemblyVersion>
             <FileVersion>$(FileVersion)</FileVersion>
             <InformationalVersion>$(InformationalVersion)</InformationalVersion>         
          </PropertyGroup>
       </Target>
       
       <Target Name="_UpdateAssemblyInfo" BeforeTargets="BeforeCompile" DependsOnTargets="PrepareMetadataFile" Condition="'$(UpdateAssemblyInfoFiles)'=='true'" Outputs="@(Fsmb_AssemblyInfoFiles)">
          <Message Text="Updating AssemblyInfo files '$(AssemblyInfoFile)'"></Message>
    
          <ItemGroup>
             <AssemblyInfoFiles Include="**\AssemblyInfo.cs" />
          </ItemGroup>
    
          <UpdateAssemblyInfo InfoFiles="@(AssemblyInfoFiles)" AssemblyVersion="$(AssemblyVersion)" Product="$(Product)"
                              Company="$(Company)" Copyright="$(Copyright)" Trademark="$(Trademark)" FileVersion="$(FileVersion)"
                              InformationalVersion="$(InformationalVersion)" Configuration="$(Configuration)"/>
       </Target>
    
       <!--### Tasks ###-->
    
       <UsingTask TaskName="UpdateAssemblyInfo" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
          <ParameterGroup>
             <InfoFiles ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
             <AssemblyVersion Required="true" />
             <Product Required="true" />
             <Company />
             <Copyright />
             <Trademark />
             <FileVersion Required="true" />
             <InformationalVersion Required="true" />
             <Configuration />
          </ParameterGroup>
          <Task>
             <Using Namespace="System.IO" />
             <Using Namespace="System.Text.RegularExpressions" />
             <Code Type="Fragment" Language="cs">
                <![CDATA[
            var attributes = new Dictionary<string, string>()
                {
                    { "AssemblyVersion", AssemblyVersion }, //If we don't find this one then we won't do anything
                    { "AssemblyProduct", Product },
                    { "AssemblyCompany", Company },
                    { "AssemblyCopyright", Copyright },
                    { "AssemblyTrademark", Trademark },                
                    { "AssemblyFileVersion", FileVersion },
                    { "AssemblyInformationalVersion", InformationalVersion },
                    { "AssemblyConfiguration", Configuration },
                };
    
              foreach (var infoFile in InfoFiles)
              {
                  //Check for an AssemblyVersion attribute so we can determine if we need to update this file 
                  var saveFile = true;
                  var lines = new List<string>(File.ReadAllLines(infoFile.ItemSpec));
                    
                  foreach (var attribute in attributes)
                  {
                      var re = @"^\[assembly\s*:\s*" + attribute.Key + @"(Attribute)?\s*\(\s*("".*""\s*)?\s*\)\s*\]$";
                      var foundMatch = false;
    		              var newValue = "[assembly: " + attribute.Key + "(\"" + attribute.Value + "\")]";
    
                      //Find the matching line, if any
                      var count = lines.Count;
                      for (int index = 0; index < count; ++index)
                      {
                          //Skip comments
                          var startComment = lines[index].IndexOf("//");
                          var line = startComment >= 0 ? lines[index].Substring(0, startComment).Trim() : lines[index];
                          if (String.IsNullOrEmpty(line))
                              continue;
                            
                          if (Regex.IsMatch(line, re))
                          {
                              Log.LogMessage(String.Format("Replacing {0}", attribute.Key), MessageImportance.Low);
                              lines[index] = newValue;
                              foundMatch = true;
                          };
                      };
    
                      //If line does not exist then add a new line of the attribute and value into file
                      if (!foundMatch)
    		              {
                          //If we didn't find AssemblyVersion then abort
                          if (attribute.Key == "AssemblyVersion")
                          {
                              saveFile = false;
                              break;
                          };
    
                          Log.LogMessage(String.Format("Adding {0}", attribute.Key), MessageImportance.Low);
                          lines.Add(newValue);
                      };                    
                  };
    
                  if (saveFile)
                  {
                      Log.LogMessage(String.Format("Updating {0}", infoFile.ItemSpec), MessageImportance.Normal);
                      File.WriteAllLines(infoFile.ItemSpec, lines);
                  };
              }
              ]]>
             </Code>
          </Task>
       </UsingTask>
    </Project>

    Drop both of these files into the root of your solution and then build. The AssemblyInfo files should be updated with the version define din the props file. If you change this file and then compile you'll see the update. Note however that VS tends to background compile which means the version in the AssemblyInfo files will actually update shortly after saving the file.

    What is missing here is the saving of the "calculated" patch version during each build. In the sample I'm storing it in the props file. Since you want to update that value you'll likely need to put it into a version.props file or something instead. You'll then need to update the targets file to read that version, increment it by one and save it back. All this needs to happen before the version is calculated later in the version file.

    Just to repeat what I started this entire discussion with, you're really going to end up doing a lot of work to get the "exact" versioning process you want. Versioning isn't simple and there are plenty of VS and build extensions providing various ways to do versioning. You should really consider looking into them but I doubt any will give you exactly what you want. If you want to roll you're own you need to have a good understanding of versioning and how builds work. The above scripts will help get you started but you still have more work to do. It really is just simpler to use the wildcard version # in the AssemblyInfo file instead.

    To get the version out for your display you'd simply fetch it from the assembly.

    //Helper method for working with custom attributes
    public T GetCustomAttribute<T> ( Assembly source ) where T: Attribute
    {
       //This code assumes you're using VS 2017+
       var attrs = source.GetCustomAttributes(typeof(T));
       if (attrs?.Any() ?? false)
       {
          return attrs.FirstOrDefault() as T;
       };  
    
       return null;
    }
    
    var asm = Assembly.GetEntryAssembly();
    
    //Assembly version which was semantic version + .0
    asm.GetName().Version
    
    //File version which is the full build version
    asm.GetCustomAttribute<AssemblyFileVersionAttribute>();
    
    //Informational version which is a string
    asm.GetCustomAttribute<AssemblyInformationalVersionAttribute>();


    Michael Taylor http://www.michaeltaylorp3.net

    Wednesday, October 24, 2018 5:42 PM
    Moderator
  • "everytime me or someone else does a build on the project."

    Are you sure that is the way you want to go? Are you using version control? If so then every time somebody does a build, irrelevant if they change anything then your assemblyinfo.cs file will be updated resulting in a checkout from your source control system or, at the very minimum, a change detection (for Git). Given that you'll likely be making changes and rebuilding your code over and over again as you fix compiler errors and logic errors then the version # will increment. Given that you are using a 3 digit value then you only get 1000 builds which doesn't sound like much until you consider how often you do builds throughout the day. Normally you would only want to do that for a "potentially releasable" build. Hence normally this is done via your build system. Although you could also configure it to only happen for Release builds in VS as well and that would alleviate the continual changes. 

    "1.4.001"

    I'm afraid you cannot do that. As I already mentioned, Windows requires version #s to be 4 parts and it'll ignore any zero prefixes. So your actual version # would be 1.4.1.0. There is nothing you can do to configure your assembly version (as seen by Windows and all other apps) to change that.

    The only string-based version you can control in this manner is AssemblyInformationalVersion. You can set your informational version to be that value and that is often how we do semantic versioning but the actual assembly/file version will not match. Even with semantic versioning, I believe, zero prefixes are ignored so your version # is 1.4.1. 

    Is there any particular reason you are really set on using this format? Even if you have to write hacks in your code to get the information from what is otherwise a Windows-defined version?

    Going back to the beginning, if you are OK accepting the fact that your version # must follow Windows convention then putting 1.4.* in your AssemblyInfo.cs file should be sufficient for it to generate versions as 1.4.1, 1.4.2 each time you build. This meets your requirement other than the zero prefix, I think.

    If you simply want a unique identifier for each build (the more common requirement) then consider using something else. In many builds we use the Julian date. Of course if you build more than once in a single day you'd get the same date. That is where build systems like AppVoyeor and TFS add revision information so normally we do something like 1.4.<juliandate>.<rev> where <rev> is incremented for each build.

    The fundamental problem you're trying to work around is that you have a base version like 1.4 and you want to auto-increment the patch #. But if you don't store that patch # somewhere then there is no way to know what the "last version" was. To store the patch # you have to update something that is checked into source control. 

    After all that, here's some starter code for working with directory.build.* to get you started down the path of generating a different version each time. But for this to work you have to update a file and ensure it is checked into your source control system after every build. This won't probably work as is but you can see the logic involved in getting versioning work based upon your requirements. This code is based upon code we use to auto-version our apps but it is experimental and is really designed to only be used for automated builds that we use.

    <!-- directory.build.props -->
    <Project>
        <!-- Solution level properties are set here -->
    	<PropertyGroup>
          <MajorVersion>1</MajorVersion>
          <MinorVersion>4</MinorVersion>
          <PatchVersion>0</PatchVersion>      	
    	  
    	  <!-- These are standard properties that project's have -->
    	  <Product>MyProduct</Product>
    	  <Company>MyCompany</Company>
    	  <Trademark></Trademark>
    	  
    	  <!-- As you switch to the newer SDK project format you don't have an AssemblyInfo file anymore so opt out as needed -->
    	  <UpdateAssemblyInfoFiles>true</UpdateAssemblyInfoFiles>
       </PropertyGroup>
    </Project>

    <!-- directory.build.targets --> 
    <?xml version="1.0" encoding="utf-8"?>
    <Project ToolsVersion="15.0" InitialTargets="SetVersion" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
        	  
       <Target Name="SetVersion">  
          <Error Condition="'$(MajorVersion)'==''" Text="Project property file doesn't specify version properties. Fix the property file before building again."/>
    
    	  <!-- These are some common properties that are either conditionally set by a build or passed on the command line -->      
    	  <PropertyGroup>
    	     <BuildLabel Condition="'$(BuildLabel)'==''"></BuildLabel>
    		 <IsPrerelease Condition="'$(IsPrerelease)'==''">false</IsPrerelease>
          </PropertyGroup>
    	  
    	  <!-- Normally the build version is the one that auto-increments and is often based upon the date.
    	  
    	       BuildVersion is normally provided by the automated build system. It is this value we generally change for each build.
    		 -->
          <PropertyGroup>
             <BuildVersion Condition="'$(BuildVersion)'==''">0</BuildVersion>
          </PropertyGroup>
    	  
          <!-- Store the version information into temporary variables       
    	  
    	       Product is semantic versioned - {major}.{minor}.{patch}
    		   Assembly has to be a full Windows version and should be valid for strong name assemblies - {major}.{minor}.{patch}.0 
    		   File is the actual version including the build # - {major}.{minor}.{patch}.{build}
    	    -->
          <PropertyGroup>
             <Temp_ProductVersion Condition="'$(Temp_ProductVersion)'==''">$(MajorVersion).$(MinorVersion).$(PatchVersion)</Temp_ProductVersion>
             <Temp_AssemblyVersion Condition="'$(Temp_AssemblyVersion)'==''">$(ProductVersion).0</Temp_AssemblyVersion>
             <Temp_FileVersion Condition="'$(Temp_FileVersion)'==''">$(ProductVersion).$(BuildVersion)</Temp_FileVersion>
          </PropertyGroup>
    
    	  <!-- For packages and other "versioning" that supports semantic versioning include any additional attributes that are commonly needed -->
          <PropertyGroup Condition="'$(SemanticVersion)'==''">
             <SemanticVersion>$(ProductVersion)</SemanticVersion>
    
             <SemanticVersion Condition="'$(BuildLabel)'!=''">$(SemanticVersion)-$(BuildLabel)</SemanticVersion>
             <SemanticVersion Condition="'$(IsPrerelease)'=='true' And '$(BuildVersion)'!='0'">$(SemanticVersion)-$(BuildVersion)</SemanticVersion>
          </PropertyGroup>
    
    	  <!-- This is the only string-based version that is supported and can technically be anything you want. -->
          <PropertyGroup Condition="'$(InformationalVersion)'==''">
             <InformationalVersion>$(SemanticVersion)</InformationalVersion>
          </PropertyGroup>
    	  
          <!-- Set final version information used by projects -->
          <PropertyGroup>
             <Version>$(SemanticVersion)</Version>
             <AssemblyVersion>$(AssemblyVersion)</AssemblyVersion>
             <FileVersion>$(FileVersion)</FileVersion>
             <InformationalVersion>$(InformationalVersion)</InformationalVersion>         
          </PropertyGroup>
       </Target>
       
       <Target Name="_UpdateAssemblyInfo" BeforeTargets="BeforeCompile" DependsOnTargets="PrepareMetadataFile" Condition="'$(UpdateAssemblyInfoFiles)'=='true'" Outputs="@(Fsmb_AssemblyInfoFiles)">
          <Message Text="Updating AssemblyInfo files '$(AssemblyInfoFile)'"></Message>
    
          <ItemGroup>
             <AssemblyInfoFiles Include="**\AssemblyInfo.cs" />
          </ItemGroup>
    
          <UpdateAssemblyInfo InfoFiles="@(AssemblyInfoFiles)" AssemblyVersion="$(AssemblyVersion)" Product="$(Product)"
                              Company="$(Company)" Copyright="$(Copyright)" Trademark="$(Trademark)" FileVersion="$(FileVersion)"
                              InformationalVersion="$(InformationalVersion)" Configuration="$(Configuration)"/>
       </Target>
    
       <!--### Tasks ###-->
    
       <UsingTask TaskName="UpdateAssemblyInfo" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
          <ParameterGroup>
             <InfoFiles ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
             <AssemblyVersion Required="true" />
             <Product Required="true" />
             <Company />
             <Copyright />
             <Trademark />
             <FileVersion Required="true" />
             <InformationalVersion Required="true" />
             <Configuration />
          </ParameterGroup>
          <Task>
             <Using Namespace="System.IO" />
             <Using Namespace="System.Text.RegularExpressions" />
             <Code Type="Fragment" Language="cs">
                <![CDATA[
            var attributes = new Dictionary<string, string>()
                {
                    { "AssemblyVersion", AssemblyVersion }, //If we don't find this one then we won't do anything
                    { "AssemblyProduct", Product },
                    { "AssemblyCompany", Company },
                    { "AssemblyCopyright", Copyright },
                    { "AssemblyTrademark", Trademark },                
                    { "AssemblyFileVersion", FileVersion },
                    { "AssemblyInformationalVersion", InformationalVersion },
                    { "AssemblyConfiguration", Configuration },
                };
    
              foreach (var infoFile in InfoFiles)
              {
                  //Check for an AssemblyVersion attribute so we can determine if we need to update this file 
                  var saveFile = true;
                  var lines = new List<string>(File.ReadAllLines(infoFile.ItemSpec));
                    
                  foreach (var attribute in attributes)
                  {
                      var re = @"^\[assembly\s*:\s*" + attribute.Key + @"(Attribute)?\s*\(\s*("".*""\s*)?\s*\)\s*\]$";
                      var foundMatch = false;
    		              var newValue = "[assembly: " + attribute.Key + "(\"" + attribute.Value + "\")]";
    
                      //Find the matching line, if any
                      var count = lines.Count;
                      for (int index = 0; index < count; ++index)
                      {
                          //Skip comments
                          var startComment = lines[index].IndexOf("//");
                          var line = startComment >= 0 ? lines[index].Substring(0, startComment).Trim() : lines[index];
                          if (String.IsNullOrEmpty(line))
                              continue;
                            
                          if (Regex.IsMatch(line, re))
                          {
                              Log.LogMessage(String.Format("Replacing {0}", attribute.Key), MessageImportance.Low);
                              lines[index] = newValue;
                              foundMatch = true;
                          };
                      };
    
                      //If line does not exist then add a new line of the attribute and value into file
                      if (!foundMatch)
    		              {
                          //If we didn't find AssemblyVersion then abort
                          if (attribute.Key == "AssemblyVersion")
                          {
                              saveFile = false;
                              break;
                          };
    
                          Log.LogMessage(String.Format("Adding {0}", attribute.Key), MessageImportance.Low);
                          lines.Add(newValue);
                      };                    
                  };
    
                  if (saveFile)
                  {
                      Log.LogMessage(String.Format("Updating {0}", infoFile.ItemSpec), MessageImportance.Normal);
                      File.WriteAllLines(infoFile.ItemSpec, lines);
                  };
              }
              ]]>
             </Code>
          </Task>
       </UsingTask>
    </Project>

    Drop both of these files into the root of your solution and then build. The AssemblyInfo files should be updated with the version define din the props file. If you change this file and then compile you'll see the update. Note however that VS tends to background compile which means the version in the AssemblyInfo files will actually update shortly after saving the file.

    What is missing here is the saving of the "calculated" patch version during each build. In the sample I'm storing it in the props file. Since you want to update that value you'll likely need to put it into a version.props file or something instead. You'll then need to update the targets file to read that version, increment it by one and save it back. All this needs to happen before the version is calculated later in the version file.

    Just to repeat what I started this entire discussion with, you're really going to end up doing a lot of work to get the "exact" versioning process you want. Versioning isn't simple and there are plenty of VS and build extensions providing various ways to do versioning. You should really consider looking into them but I doubt any will give you exactly what you want. If you want to roll you're own you need to have a good understanding of versioning and how builds work. The above scripts will help get you started but you still have more work to do. It really is just simpler to use the wildcard version # in the AssemblyInfo file instead.

    To get the version out for your display you'd simply fetch it from the assembly.

    //Helper method for working with custom attributes
    public T GetCustomAttribute<T> ( Assembly source ) where T: Attribute
    {
       //This code assumes you're using VS 2017+
       var attrs = source.GetCustomAttributes(typeof(T));
       if (attrs?.Any() ?? false)
       {
          return attrs.FirstOrDefault() as T;
       };  
    
       return null;
    }
    
    var asm = Assembly.GetEntryAssembly();
    
    //Assembly version which was semantic version + .0
    asm.GetName().Version
    
    //File version which is the full build version
    asm.GetCustomAttribute<AssemblyFileVersionAttribute>();
    
    //Informational version which is a string
    asm.GetCustomAttribute<AssemblyInformationalVersionAttribute>();


    Michael Taylor http://www.michaeltaylorp3.net

    Michael,

    First I want to say thank you very much for this. This is a very good template to use.

    Next I want to say that no it doesn't have to be in the 1.4.001 format. I'm doing this as a request for a client but I can easily explain that format won't work. I am new to this entire process so the extra numbers thing is something I wasn't aware that couldn't be done. As long as it internals by 1.4.1 then 1.4.2 etc. that is fine.

    As for doing it each build that is fine too. The client and I have come to an understand that I'm going to build once and then he will do a build once so it will interval by 2 versions each time so that is fine as well. Hopefully that clears up things a little bit.

    Wednesday, October 24, 2018 8:41 PM