static void

.Net Versioning

Published Wednesday 23 July 2014


The mscorlib System.Version class accepts 2, 3 or 4 integers to represent a version.
var v = new Version(2014010110, 7383783, 38989899, 893839893);
Console.WriteLine(v.ToString()); //shows 2014010110.7383783.38989899.893839893

The values are Major.Minor[.Build[.Revision]]

As we'll see shortly, actual assembly versions are much more limited!


Semantic versioning can be mapped into the .net scheme.

In SemVer, the scheme is Major.Minor.Patch.

So .net Build is equivalent to semver Patch, and revision, which is optional anyway, is disregarded. (The original .net convention was that build used the same source with different symbols).

Version attributes

The version attributes are normally in Properties/AssemblyInfo.cs (but could be anywhere).
You can also access AssemblyVersion and AssemblyFileVersion via the project properties - application - [Assembly Information...] button.

There are 3:

//CLR uses this as the version 
[assembly: AssemblyVersion("")]

//Not used by CLR, often the specific build
[assembly: AssemblyFileVersion("")]

//If not present, == AssemblyVersion. 
[assembly: AssemblyInformationalVersion("v1.0 RC")]

AssemblyInformationalVersion used to error if it wasn't a System.Version (all ints). Since Visual Studio 2010, you can put in free-form strings, which is useful for tags like "RC".

To access these via code:

var executingAssembly = Assembly.GetExecutingAssembly();
var ver = executingAssembly.GetName().Version; //AssemblyVersion
var fv = System.Diagnostics.FileVersionInfo.GetVersionInfo(executingAssembly.Location);
Console.WriteLine(fv.FileVersion); //AssemblyFileVersion
Console.WriteLine(fv.ProductVersion); //AssemblyInformationalVersion

There is also a fv.ProductMajorPart and fv.ProductMinorPart but these aren't populated if the AssemblyInformationalVersion can't be parsed into a System.Version.

The values- major, minor, build, revision - are ints, up to 2,147,483,647. But there's a big gotcha. For operating system reasons, the compiler limits values to 65,534, int16.

For AssemblyVersion, you get a CSC Error: "Error emitting 'System.Reflection.AssemblyVersionAttribute' attribute -- 'The version specified '65536.65535.65535.65535' is invalid'"

For AssemblyFileVersion, you get a CSC Warning    "Assembly generation -- The version '65536.65535.65535.65535' specified for the 'file version' is not in the normal '' format". It will build, at least.

Versioning Strategy

There are several candidates for traceable build and revision numbers, but none are "semver" (both build and revision are significant).

Both build name and changeset number might be better set in AssemblyInformationalVersion.


For AssemblyVersion only, you can use wildcards for build and revision.

If you use it for file version, you get a warning:
CSC : warning CS1607: Assembly generation -- The version '1.2.0.*' specified for the 'file version' is not in the normal '' format

If you build twice without changing, the revision goes up. If you build the next day without changes, the build goes up.

Wildcards are pretty useless.

Build Tasks

Build tasks run after source control get-latest, before compilation. They find the AssemblyInfo.cs files, flip the readonly flag, and find and replace the AssemblyFileVersion, then compile. The changed AssemblyInfo file should not be checked in. The process is not run in developer builds, only in "publish" builds.

MSBuild Extension Pack is a set of msbuild tasks, which is also available as a Nuget package (MSBuild.Extension.Pack). One task, MSBuild.ExtensionPack.VisualStudio.TfsVersion, edits the AssemblyFileVersion given a date or tfs-format build name.

Another project, Community TFS Build Extensions, made by some of the same people, hooks up into TFS 2012/2013 xaml workflows and includes a TfsVersion build activity.

Previously: Forms Authentication with claims (07 Mar 2014)