Global .NET Versioning Strategy – AssemblyInformationalVersion

Ever heard of a third (optional) versioning attribute in the AssemblyInfo files: AssemblyInformationalVersion. No? Please read!

Without a methodical (assembly) version numbering strategy, the ability to determine what changes were included in which version is lost. In my opinion, you always need to know exactly which source files went into which build and which version of the sofware is currently deployed in a specific environment. A random version numbering system creates confusion and will soon or later cause deployment risks. It will become a nightmare to fetch the exact source files to reproduce a bug from production.

All versioning of .NET assemblies that use the common language runtime is done at the assembly level. The specific version of an assembly and the versions of dependent assemblies are recorded in the assembly’s manifest. The default version policy for the runtime is that applications run only with the versions they were built and tested with, unless overridden by explicit version policy in configuration files.

Each .NET project has an AssemblyInfo file which contains an AssemblyVersion attribute and an AssemblyFileVersion attribute.

  • AssemblyVersion: this is the version number used by the .NET framework during build and at runtime to locate, link and load the assemblies. When you add a reference to any assembly in your project, it is this version number which gets embedded. At runtime, the CLR looks for assembly with this version number to load. But remember this version is used along with name, public key token and culture information only if the assemblies are strong-named signed. If assemblies are not strong-named signed, only file names are used for loading.
  • AssemblyFileVersion: This is the version number given to a file as in file system. It is displayed by Windows Explorer. It’s never used by .NET framework or runtime for referencing.

But what about this difference between AssemblyVersion and AssemblyFileVersion? Many times, I see that the same version is applied to both attributes … but why are these two (different) attributes provided by the .NET Framework? The AssemblyVersion should be the public version of an entire software application, while the AssemblyFileVersion is more the version of a specific component which may only be a small part of the entire application. The AssemblyFileVersion is the best place to put extra build version information which can be important for patching individual components of a software application.

Please follow the Semantic Versioning recommendations to dictate how the AssemblyVersion should be assigned and incremented. For the AssemblyFileVersion, I tend to include specific build information. Often, you will need to build (and test) a number of time a specific SemVer version of your software.

For example: release 1 of a software application could have the AssemblyVersion set to 1.0.0 (all components), while the AssemblyFileVersion of the individual components could be set to 1.0.15234.2 which refers to a unique build number of the build system and is linked to a particular date and a revision: “15” = year 2015; “234” = day number in 2015; “2” = second build processed that day. This also allows to later patch individual components in production with a similar AssemblyVersion (1.0.0), but a different AssemblyFileVersion (1.0.15235.1).

So, let’s try to apply this to a test project in Visual Studio and see the assembly details after building the project …


Now you should be confused! Why does the Product Version display the AssemblyFileVersion and where’s the AssemblyVersion? The problem here is that a new Visual Studio project doesn’t include a third version attribute AssemblyInformationalVersion which is intended to represent the public version of your entire software application. Note that the CLR doesn’t care about this third (optional) version attribute. In short, the same Semantic Versioning rules of AssemblyVersion should be applied to AssemblyInformationalVersion.


Aha! This looks much better right? Now it’s also easy to extract this metadata from your deployed assemblies and this information can be nicely listed in the about box of your software. The only issue with this approach is that the AssemblyFileVersion doesn’t include the “patch” number (Semantic Versioning) of the AssemblyVersion, but this can be ignored with the fact that the AssemblyFileVersion will be unique and can be linked to a unique build run in the build system. This way of working is my personal interpretation of how versioning can be properly applied in complex software applications and doesn’t reflect official guidelines from Microsoft. My goal here is to make software developers aware of the potential risks of not having a clear versioning strategy.

Now, forget about manually setting version information in the AssemblyInfo files and never ever release software from a local Visual Studio build. In a streamlined build process, generating unique build and version numbers are centrally coordinated. For effective troubleshooting and traceability, it’s imperative that the generated assemblies are stamped with a unique identifier that can be easily traced back to a system build number.

In a next post I will talk about how you can achieve this global .NET versioning strategy with the new build system in TFS 2015.

5 Responses to Global .NET Versioning Strategy – AssemblyInformationalVersion

  1. […] my previous blogpost I wrote about adopting a Global .NET Versioning Strategy and the existence of a third (optional) […]

  2. […] Global .NET Versioning Strategy – AssemblyInformationalVersion  by Pieter Gheysens […]

  3. Real Semver allows a prerelease tag like this “1.2.4-beta04”. Semver also allows metadata like build number, date, commit or changeset like this “1.0.0+20130313144700”.

    AssemblyInformationalVersion is the only field that allows anything otherthan the classic version notation such as AssemblyVersion and AssemblyFileVersion only allow the strict version notation. And every part is limited to 65536 I believe (that why we need weird notation for dates right?).

    You sugggest setting AssemblyInformationalVersion and AssemblyVersion to the same version, but then we loose this powerful semver feature/idea. And all that’s left is putting some date representation of build number into the AssemblyFileVersion (as long as it’s in the form of
    Maybe AssemblyInformationalVersion should start with AssemblyVersion but allow for the tag and metadata to be appended?

    [assembly: AssemblyVersion(“2.0.1”)]
    [assembly: AssemblyFileVersion(“2.0.15324.3”)]
    [assembly: AssemblyInformationalVersion(“2.0.1-beta01+CS2341”)]

    When packaging up a nupkg, the AssemblyInformationalVersion will be used. Which is great because that way you can create pre release packages. But it might not be easy to configure prerelease tags for your automated builds.

    The least we can say, it’s a mess, it’s confusing and the framework was not built with all this in mind. To make things worse, we have an actual job to do instead of trying to figure out these futile details🙂

  4. The global assembly info is a good idea. By the way, the Assembly Versioner and Info Updater can help manager project/assembly versions and other info for all projects/assemblies in a single UI:

    Visual Smarter has a lot more features than the above two.

  5. Sharing a common assembly version/info file is a good idea if all assemblies/projects are supposed to have the same info/version. Otherwise, the Assembly Versioner and Info Updater can come to rescue at the last minute:

    Visual Smarter has a lot more features than the above two.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: