886

There are three assembly version attributes. What are differences? Is it ok if I use AssemblyVersion and ignore the rest?


MSDN says:

  • AssemblyVersion:

    Specifies the version of the assembly being attributed.

  • AssemblyFileVersion:

    Instructs a compiler to use a specific version number for the Win32 file version resource. The Win32 file version is not required to be the same as the assembly's version number.

  • AssemblyInformationalVersion:

    Defines additional version information for an assembly manifest.


This is a follow-up to What are the best practices for using Assembly Attributes?

Ian Kemp
  • 24,155
  • 16
  • 97
  • 121
Jakub Šturc
  • 32,938
  • 24
  • 85
  • 107

7 Answers7

930

AssemblyVersion

Where other assemblies that reference your assembly will look. If this number changes, other assemblies have to update their references to your assembly! Only update this version, if it breaks backward compatibility. The AssemblyVersion is required.

I use the format: major.minor. This would result in:

[assembly: AssemblyVersion("1.0")]

If you're following SemVer strictly then this means you only update when the major changes, so 1.0, 2.0, 3.0, etc.

AssemblyFileVersion

Used for deployment. You can increase this number for every deployment. It is used by setup programs. Use it to mark assemblies that have the same AssemblyVersion, but are generated from different builds.

In Windows, it can be viewed in the file properties.

The AssemblyFileVersion is optional. If not given, the AssemblyVersion is used.

I use the format: major.minor.patch.build, where I follow SemVer for the first three parts and use the buildnumber of the buildserver for the last part (0 for local build). This would result in:

[assembly: AssemblyFileVersion("1.3.2.254")]

Be aware that System.Version names these parts as major.minor.build.revision!

AssemblyInformationalVersion

The Product version of the assembly. This is the version you would use when talking to customers or for display on your website. This version can be a string, like '1.0 Release Candidate'.

The AssemblyInformationalVersion is optional. If not given, the AssemblyFileVersion is used.

I use the format: major.minor[.patch] [revision as string]. This would result in:

[assembly: AssemblyInformationalVersion("1.0 RC1")]
Rémy van Duijkeren
  • 9,417
  • 1
  • 14
  • 10
  • 1
    A situation: ASM - an assembly project (.DLL/.EXE). SETUP - a setup project. Note that if [SETUP targets ASM] AND [ASM's AssemblyFileVersion NOT updated] AND [ASM's binaries don't change (e.g. only content files updated)]; I've had issues with SETUP not taking the latest binaries (with the latest versions specified). This caused issues with `Application.ProductVersion` and when accessing `AssemblyInformationalVersion` via reflection. – Jake Berger Mar 02 '12 at 20:55
  • 4
    For AssemblyFileVersion, "If possible, let it be generated by MSBuild" - Why? You just went on to explain a good reason to manually control it :) – mo. Jan 04 '13 at 17:50
  • 3
    The warning on the AssemblyInformationalVersion format still exists in VS2010 today (May 21, 2013) and your link is dead. – reinierpost May 21 '13 at 13:10
  • 22
    Unfortunately the [Version Class](http://msdn.microsoft.com/en-us/library/system.version.aspx) defines `major.minor[.build[.revision]]` and not `major.minor.revision.build` so in the given answer the build and revision numbers would be swapped if you were using the class properties or `System.Reflection.Assembly.GetExecutingAssembly().GetName().Version` to detect the build and revision numbers. – thinkOfaNumber Jan 21 '14 at 02:21
  • 9
    @thinkOfaNumber Your right about the Version Class, but that's Microsoft way of versioning. I personally think it is strange to not have the buildnumber at the end and that's why I only put my format as an example, based on [Semantic Versioning](http://semver.org/spec/v2.0.0.html). Your free to use the Microsoft way or your own way of course. – Rémy van Duijkeren Jan 25 '14 at 22:49
  • 1
    @mo The remark "If possible, let it be generated by MSBuild" was meant especially for the build part (and optional the revision part) of the version. Sorry for not being clear. – Rémy van Duijkeren Jan 25 '14 at 23:01
  • 2
    @RémyvanDuijkeren I agree, I like to use semantic versioning as well, but it's difficult to mix with the defined `Version` class unless you don't use Microsoft's definitions, which other users of your assembly might be expecting. The [remarks here](http://msdn.microsoft.com/en-us/library/system.version(v=vs.110).aspx) sate "A difference in build number represents a recompilation of the same source." – thinkOfaNumber Jan 26 '14 at 23:29
  • 2
    Visual Studio shows two versions in its reference properties: Version and RuntimeVersion. How does that relate to the versions listed here? Is the RuntimeVersion inserted automatically? – Fred Chateau Mar 04 '14 at 03:50
  • 7
    Should be noted that for `AssemblyInformationalVersion`, if omitted, `AssemblyFileVersion` is used. **Then** `AssemblyVersion` if both are omitted. – Drazen Bjelovuk Aug 30 '16 at 00:18
  • 1
    AssemblyInformationalVersion==ProductVersion is a string - so this is the place where one can put all sorts ofthings, like date or build number. I was searching for this info for some time, so I add it here. – Andreas Reiff Aug 25 '17 at 07:20
  • A sample: My current version is 3.0.. what if I provide a new API (e.g. new Interface) in 3.1 (not breaking but new feature) but the AssemblyVersion does not change (3.0) and someone wants to use this interface? Can this not be a problem? Do I not have to change the AssemblyVersion to 3.1? I have downloaded several famous packages from nuget and have found a mix. Some developers are changing the minor (and some also the patch) version and some not. – dennis Sep 16 '20 at 19:22
  • 1
    @dennis If the AssemblyVersion doesn't change than it communicates to other libs, that are dependent on it, that they can **upgrading safely** without breaking/recompiling their code. It doesn't mean downgrading is safe. I use only the _Major_ (=semver) and sometimes the _Major_ and _Minor_ for the AssemblyVersion. I let it depend on the type and maturate of the project. If there are a lot of breaking changes in a short time (usually the start of a project) then I also use the Minor, to prevent high version numbers. When things stabilize, I only change the _Major_. – Rémy van Duijkeren Sep 20 '20 at 06:00
611

Versioning of assemblies in .NET can be a confusing prospect given that there are currently at least three ways to specify a version for your assembly.

Here are the three main version-related assembly attributes:

// Assembly mscorlib, Version 2.0.0.0
[assembly: AssemblyFileVersion("2.0.50727.3521")]
[assembly: AssemblyInformationalVersion("2.0.50727.3521")]
[assembly: AssemblyVersion("2.0.0.0")]

By convention, the four parts of the version are referred to as the Major Version, Minor Version, Build, and Revision.

The AssemblyFileVersion is intended to uniquely identify a build of the individual assembly

Typically you’ll manually set the Major and Minor AssemblyFileVersion to reflect the version of the assembly, then increment the Build and/or Revision every time your build system compiles the assembly. The AssemblyFileVersion should allow you to uniquely identify a build of the assembly, so that you can use it as a starting point for debugging any problems.

On my current project we have the build server encode the changelist number from our source control repository into the Build and Revision parts of the AssemblyFileVersion. This allows us to map directly from an assembly to its source code, for any assembly generated by the build server (without having to use labels or branches in source control, or manually keeping any records of released versions).

This version number is stored in the Win32 version resource and can be seen when viewing the Windows Explorer property pages for the assembly.

The CLR does not care about nor examine the AssemblyFileVersion.

The AssemblyInformationalVersion is intended to represent the version of your entire product

The AssemblyInformationalVersion is intended to allow coherent versioning of the entire product, which may consist of many assemblies that are independently versioned, perhaps with differing versioning policies, and potentially developed by disparate teams.

“For example, version 2.0 of a product might contain several assemblies; one of these assemblies is marked as version 1.0 since it’s a new assembly that didn’t ship in version 1.0 of the same product. Typically, you set the major and minor parts of this version number to represent the public version of your product. Then you increment the build and revision parts each time you package a complete product with all its assemblies.” — Jeffrey Richter, [CLR via C# (Second Edition)] p. 57

The CLR does not care about nor examine the AssemblyInformationalVersion.

The AssemblyVersion is the only version the CLR cares about (but it cares about the entire AssemblyVersion)

The AssemblyVersion is used by the CLR to bind to strongly named assemblies. It is stored in the AssemblyDef manifest metadata table of the built assembly, and in the AssemblyRef table of any assembly that references it.

This is very important, because it means that when you reference a strongly named assembly, you are tightly bound to a specific AssemblyVersion of that assembly. The entire AssemblyVersion must be an exact match for the binding to succeed. For example, if you reference version 1.0.0.0 of a strongly named assembly at build-time, but only version 1.0.0.1 of that assembly is available at runtime, binding will fail! (You will then have to work around this using Assembly Binding Redirection.)

Confusion over whether the entire AssemblyVersion has to match. (Yes, it does.)

There is a little confusion around whether the entire AssemblyVersion has to be an exact match in order for an assembly to be loaded. Some people are under the false belief that only the Major and Minor parts of the AssemblyVersion have to match in order for binding to succeed. This is a sensible assumption, however it is ultimately incorrect (as of .NET 3.5), and it’s trivial to verify this for your version of the CLR. Just execute this sample code.

On my machine the second assembly load fails, and the last two lines of the fusion log make it perfectly clear why:

.NET Framework Version: 2.0.50727.3521
---
Attempting to load assembly: Rhino.Mocks, Version=3.5.0.1337, Culture=neutral, PublicKeyToken=0b3305902db7183f
Successfully loaded assembly: Rhino.Mocks, Version=3.5.0.1337, Culture=neutral, PublicKeyToken=0b3305902db7183f
---
Attempting to load assembly: Rhino.Mocks, Version=3.5.0.1336, Culture=neutral, PublicKeyToken=0b3305902db7183f
Assembly binding for  failed:
System.IO.FileLoadException: Could not load file or assembly 'Rhino.Mocks, Version=3.5.0.1336, Culture=neutral, 
PublicKeyToken=0b3305902db7183f' or one of its dependencies. The located assembly's manifest definition 
does not match the assembly reference. (Exception from HRESULT: 0x80131040)
File name: 'Rhino.Mocks, Version=3.5.0.1336, Culture=neutral, PublicKeyToken=0b3305902db7183f'

=== Pre-bind state information ===
LOG: User = Phoenix\Dani
LOG: DisplayName = Rhino.Mocks, Version=3.5.0.1336, Culture=neutral, PublicKeyToken=0b3305902db7183f
 (Fully-specified)
LOG: Appbase = [...]
LOG: Initial PrivatePath = NULL
Calling assembly : AssemblyBinding, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.
===
LOG: This bind starts in default load context.
LOG: No application configuration file found.
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v2.0.50727\config\machine.config.
LOG: Post-policy reference: Rhino.Mocks, Version=3.5.0.1336, Culture=neutral, PublicKeyToken=0b3305902db7183f
LOG: Attempting download of new URL [...].
WRN: Comparing the assembly name resulted in the mismatch: Revision Number
ERR: Failed to complete setup of assembly (hr = 0x80131040). Probing terminated.

I think the source of this confusion is probably because Microsoft originally intended to be a little more lenient on this strict matching of the full AssemblyVersion, by matching only on the Major and Minor version parts:

“When loading an assembly, the CLR will automatically find the latest installed servicing version that matches the major/minor version of the assembly being requested.” — Jeffrey Richter, [CLR via C# (Second Edition)] p. 56

This was the behaviour in Beta 1 of the 1.0 CLR, however this feature was removed before the 1.0 release, and hasn’t managed to re-surface in .NET 2.0:

“Note: I have just described how you should think of version numbers. Unfortunately, the CLR doesn’t treat version numbers this way. [In .NET 2.0], the CLR treats a version number as an opaque value, and if an assembly depends on version 1.2.3.4 of another assembly, the CLR tries to load version 1.2.3.4 only (unless a binding redirection is in place). However, Microsoft has plans to change the CLR’s loader in a future version so that it loads the latest build/revision for a given major/minor version of an assembly. For example, on a future version of the CLR, if the loader is trying to find version 1.2.3.4 of an assembly and version 1.2.5.0 exists, the loader with automatically pick up the latest servicing version. This will be a very welcome change to the CLR’s loader — I for one can’t wait.” — Jeffrey Richter, [CLR via C# (Second Edition)] p. 164 (Emphasis mine)

As this change still hasn’t been implemented, I think it’s safe to assume that Microsoft had back-tracked on this intent, and it is perhaps too late to change this now. I tried to search around the web to find out what happened with these plans, but I couldn’t find any answers. I still wanted to get to the bottom of it.

So I emailed Jeff Richter and asked him directly — I figured if anyone knew what happened, it would be him.

He replied within 12 hours, on a Saturday morning no less, and clarified that the .NET 1.0 Beta 1 loader did implement this ‘automatic roll-forward’ mechanism of picking up the latest available Build and Revision of an assembly, but this behaviour was reverted before .NET 1.0 shipped. It was later intended to revive this but it didn’t make it in before the CLR 2.0 shipped. Then came Silverlight, which took priority for the CLR team, so this functionality got delayed further. In the meantime, most of the people who were around in the days of CLR 1.0 Beta 1 have since moved on, so it’s unlikely that this will see the light of day, despite all the hard work that had already been put into it.

The current behaviour, it seems, is here to stay.

It is also worth noting from my discussion with Jeff that AssemblyFileVersion was only added after the removal of the ‘automatic roll-forward’ mechanism — because after 1.0 Beta 1, any change to the AssemblyVersion was a breaking change for your customers, there was then nowhere to safely store your build number. AssemblyFileVersion is that safe haven, since it’s never automatically examined by the CLR. Maybe it’s clearer that way, having two separate version numbers, with separate meanings, rather than trying to make that separation between the Major/Minor (breaking) and the Build/Revision (non-breaking) parts of the AssemblyVersion.

The bottom line: Think carefully about when you change your AssemblyVersion

The moral is that if you’re shipping assemblies that other developers are going to be referencing, you need to be extremely careful about when you do (and don’t) change the AssemblyVersion of those assemblies. Any changes to the AssemblyVersion will mean that application developers will either have to re-compile against the new version (to update those AssemblyRef entries) or use assembly binding redirects to manually override the binding.

  • Do not change the AssemblyVersion for a servicing release which is intended to be backwards compatible.
  • Do change the AssemblyVersion for a release that you know has breaking changes.

Just take another look at the version attributes on mscorlib:

// Assembly mscorlib, Version 2.0.0.0
[assembly: AssemblyFileVersion("2.0.50727.3521")]
[assembly: AssemblyInformationalVersion("2.0.50727.3521")]
[assembly: AssemblyVersion("2.0.0.0")]

Note that it’s the AssemblyFileVersion that contains all the interesting servicing information (it’s the Revision part of this version that tells you what Service Pack you’re on), meanwhile the AssemblyVersion is fixed at a boring old 2.0.0.0. Any change to the AssemblyVersion would force every .NET application referencing mscorlib.dll to re-compile against the new version!

Daniel Fortunov
  • 38,854
  • 23
  • 76
  • 101
  • 13
    Great answer. I think the most important point you made--and what MS should be explicitly recommending--is to make changes to the AssemblyVersion **if and only if** the new version breaks backward compatibility. – mwolfe02 Sep 20 '13 at 13:43
  • 1
    One of the questions I repeatedly ask myself is when should I change each of these version numbers, your bullet points on AssemblyVersion added good clarity to this and the whole answer was an interesting read. – RyanfaeScotland Jun 04 '15 at 08:30
43

AssemblyVersion pretty much stays internal to .NET, while AssemblyFileVersion is what Windows sees. If you go to the properties of an assembly sitting in a directory and switch to the version tab, the AssemblyFileVersion is what you'll see up top. If you sort files by version, this is what's used by Explorer.

The AssemblyInformationalVersion maps to the "Product Version" and is meant to be purely "human-used".

AssemblyVersion is certainly the most important, but I wouldn't skip AssemblyFileVersion, either. If you don't provide AssemblyInformationalVersion, the compiler adds it for you by stripping off the "revision" piece of your version number and leaving the major.minor.build.

Grant Palin
  • 4,418
  • 3
  • 34
  • 55
Bob King
  • 23,928
  • 6
  • 52
  • 65
25

AssemblyInformationalVersion and AssemblyFileVersion are displayed when you view the "Version" information on a file through Windows Explorer by viewing the file properties. These attributes actually get compiled in to a VERSION_INFO resource that is created by the compiler.

AssemblyInformationalVersion is the "Product version" value. AssemblyFileVersion is the "File version" value.

The AssemblyVersion is specific to .NET assemblies and is used by the .NET assembly loader to know which version of an assembly to load/bind at runtime.

Out of these, the only one that is absolutely required by .NET is the AssemblyVersion attribute. Unfortunately it can also cause the most problems when it changes indiscriminately, especially if you are strong naming your assemblies.

Dejan
  • 6,349
  • 4
  • 51
  • 88
Scott Dorman
  • 40,345
  • 11
  • 74
  • 107
9

To keep this question current it is worth highlighting that AssemblyInformationalVersion is used by NuGet and reflects the package version including any pre-release suffix.

For example an AssemblyVersion of 1.0.3.* packaged with the asp.net core dotnet-cli

dotnet pack --version-suffix ci-7 src/MyProject

Produces a package with version 1.0.3-ci-7 which you can inspect with reflection using:

CustomAttributeExtensions.GetCustomAttribute<AssemblyInformationalVersionAttribute>(asm);
KCD
  • 8,614
  • 3
  • 57
  • 66
7

It's worth noting some other things:

  1. As shown in Windows Explorer Properties dialog for the generated assembly file, there are two places called "File version". The one seen in the header of the dialog shows the AssemblyVersion, not the AssemblyFileVersion.

    In the Other version information section, there is another element called "File Version". This is where you can see what was entered as the AssemblyFileVersion.

  2. AssemblyFileVersion is just plain text. It doesn't have to conform to the numbering scheme restrictions that AssemblyVersion does (<build> < 65K, e.g.). It can be 3.2.<release tag text>.<datetime>, if you like. Your build system will have to fill in the tokens.

    Moreover, it is not subject to the wildcard replacement that AssemblyVersion is. If you just have a value of "3.0.1.*" in the AssemblyInfo.cs, that is exactly what will show in the Other version information->File Version element.

  3. I don't know the impact upon an installer of using something other than numeric file version numbers, though.

wonea
  • 3,987
  • 17
  • 71
  • 134
DavidM
  • 81
  • 1
  • 4
2

When a assembly' s AssemblyVersion is changed, If it has strong name, the referencing assemblies need to be recompiled, otherwise the assembly does not load! If it does not have strong name, if not explicitly added to project file, it will not be copied to output directory when build so you may miss depending assemblies, especially after cleaning the output directory.

linquize
  • 18,414
  • 9
  • 55
  • 78
  • This is very interesting! Could you elaborate bit on the "will not be copied to output directory" part? Perhaps a link to where this behavior is defined. I never understood why some indirect dependencies were copied sometimes, but not always. This must be 100% related to it. – julealgon Jun 27 '14 at 16:13