Le versionnement des assemblys dans .NET peut être une perspective déroutante étant donné qu'il existe actuellement au moins trois façons de spécifier une version pour votre assembly.
Voici les trois principaux attributs d'assembly liés à la version:
// 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")]
Par convention, les quatre parties de la version sont appelés la version majeure , version mineure , Construire et révision .
Le AssemblyFileVersion
est destiné à identifier de manière unique une version de l' assemblage individuel
En règle générale, vous définissez manuellement les versions AssemblyFile Major et Minor pour refléter la version de l'assembly, puis incrémentez la génération et / ou la révision à chaque fois que votre système de build compile l'assembly. AssemblyFileVersion doit vous permettre d'identifier de manière unique une génération de l'assembly, afin que vous puissiez l'utiliser comme point de départ pour déboguer tout problème.
Sur mon projet actuel, le serveur de génération code le numéro de liste de modifications de notre référentiel de contrôle de source dans les parties Build et Revision de AssemblyFileVersion. Cela nous permet de mapper directement d'un assembly à son code source, pour tout assembly généré par le serveur de build (sans avoir à utiliser des étiquettes ou des branches dans le contrôle de code source, ou à conserver manuellement les enregistrements des versions publiées).
Ce numéro de version est stocké dans la ressource de version Win32 et peut être vu lors de l'affichage des pages de propriétés de l'Explorateur Windows pour l'assembly.
Le CLR ne se soucie ni n'examine la AssemblyFileVersion.
Le AssemblyInformationalVersion
est destiné à représenter la version de l'ensemble de votre produit
AssemblyInformationalVersion est destiné à permettre un versionnage cohérent de l'ensemble du produit, qui peut être composé de nombreux assemblys qui sont versionnés indépendamment, peut-être avec des politiques de versioning différentes, et potentiellement développés par des équipes disparates.
«Par exemple, la version 2.0 d'un produit peut contenir plusieurs assemblages; l'un de ces assemblages est marqué comme version 1.0 car il s'agit d'un nouvel assemblage qui n'a pas été livré dans la version 1.0 du même produit. En règle générale, vous définissez les parties principales et mineures de ce numéro de version pour représenter la version publique de votre produit. Ensuite, vous incrémentez les parties de construction et de révision chaque fois que vous conditionnez un produit complet avec tous ses assemblages. » - Jeffrey Richter, [CLR via C # (deuxième édition)] p. 57
Le CLR ne se soucie pas ni n'examine la AssemblyInformationalVersion.
C'est AssemblyVersion
la seule version dont le CLR se soucie (mais il se soucie de l'ensemble AssemblyVersion
)
AssemblyVersion est utilisé par le CLR pour se lier à des assemblys fortement nommés. Il est stocké dans la table de métadonnées du manifeste AssemblyDef de l'assembly généré et dans la table AssemblyRef de tout assembly qui le référence.
Ceci est très important, car cela signifie que lorsque vous référencez un assemblage fortement nommé, vous êtes étroitement lié à une version d'assemblage spécifique de cet assemblage. L'ensemble AssemblyVersion doit être une correspondance exacte pour que la liaison réussisse. Par exemple, si vous référencez la version 1.0.0.0 d'un assembly fortement nommé au moment de la construction, mais que seule la version 1.0.0.1 de cet assembly est disponible au moment de l'exécution, la liaison échouera! (Vous devrez ensuite contourner ce problème en utilisant la redirection de liaison d'assembly .)
Confusion quant à savoir si l'ensemble AssemblyVersion
doit correspondre. (Oui.)
Il existe une petite confusion quant à savoir si l'ensemble AssemblyVersion doit être une correspondance exacte pour qu'un assemblage soit chargé. Certaines personnes croient à tort que seules les parties majeures et mineures de la version Assembly doivent correspondre pour que la liaison réussisse. Il s'agit d'une hypothèse raisonnable, mais elle est finalement incorrecte (à partir de .NET 3.5), et il est trivial de vérifier cela pour votre version du CLR. Exécutez simplement cet exemple de code .
Sur ma machine, la deuxième charge d'assemblage échoue et les deux dernières lignes du journal de fusion expliquent parfaitement pourquoi:
.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.
Je pense que la source de cette confusion est probablement due au fait que Microsoft avait initialement l'intention d'être un peu plus indulgent sur cette correspondance stricte de la version complète de AssemblyVersion, en ne faisant correspondre que les parties des versions Major et Minor:
"Lors du chargement d'un assemblage, le CLR trouvera automatiquement la dernière version de maintenance installée qui correspond à la version principale / mineure de l'assemblage demandé." - Jeffrey Richter, [CLR via C # (deuxième édition)] p. 56
C'était le comportement de la version bêta 1 du CLR 1.0, mais cette fonctionnalité a été supprimée avant la version 1.0 et n'a pas réussi à refaire surface dans .NET 2.0:
«Remarque: je viens de décrire comment vous devriez penser aux numéros de version. Malheureusement, le CLR ne traite pas les numéros de version de cette façon. [Dans .NET 2.0], le CLR traite un numéro de version comme une valeur opaque et si un assembly dépend de la version 1.2.3.4 d'un autre assembly, le CLR essaie de charger la version 1.2.3.4 uniquement (sauf si une redirection de liaison est en place ). Cependant,
Microsoft prévoit de modifier le chargeur du CLR dans une future version afin qu'il charge la dernière version / révision pour une version majeure / mineure donnée d'un assemblage. Par exemple, sur une future version du CLR, si le chargeur essaie de trouver la version 1.2.3.4 d'un assemblage et que la version 1.2.5.0 existe, le chargeur récupère automatiquement la dernière version de maintenance. Ce sera un changement très apprécié dans le chargeur du CLR - pour ma part, je ne peux pas attendre. » - Jeffrey Richter, [CLR via C # (deuxième édition)] p. 164 (c'est moi qui souligne)
Comme ce changement n'a toujours pas été mis en œuvre, je pense qu'il est prudent de supposer que Microsoft a fait marche arrière sur cette intention, et il est peut-être trop tard pour changer cela maintenant. J'ai essayé de chercher sur le Web pour savoir ce qui s'est passé avec ces plans, mais je n'ai trouvé aucune réponse. Je voulais toujours aller au fond des choses.
J'ai donc envoyé un courriel à Jeff Richter et lui ai demandé directement - je me suis dit que si quelqu'un savait ce qui s'était passé, ce serait lui.
Il a répondu dans les 12 heures, un samedi matin au moins, et a précisé que le chargeur .NET 1.0 Beta 1 avait mis en œuvre ce mécanisme de `` roll-forward automatique '' pour récupérer la dernière version disponible de la construction et de la révision d'un assemblage, mais ce comportement était rétabli avant l'expédition de .NET 1.0. Il était plus tard destiné à faire revivre cela, mais il n'a pas réussi avant la livraison du CLR 2.0. Puis vint Silverlight, qui était prioritaire pour l'équipe CLR, cette fonctionnalité a donc été retardée davantage. Entre-temps, la plupart des gens qui étaient là à l'époque de CLR 1.0 Beta 1 ont depuis évolué, il est donc peu probable que cela voit le jour, malgré tout le travail acharné qui a déjà été fait.
Il semble que le comportement actuel soit là pour rester.
Il est également intéressant de noter de ma discussion avec Jeff que AssemblyFileVersion n'a été ajouté qu'après la suppression du mécanisme de `` roll-forward automatique '' - car après 1.0 Beta 1, toute modification de AssemblyVersion était un changement de rupture pour vos clients, il y avait alors nulle part où stocker votre numéro de build en toute sécurité. AssemblyFileVersion est ce refuge, car il n'est jamais automatiquement examiné par le CLR. Peut-être que c'est plus clair de cette façon, avoir deux numéros de version séparés, avec des significations distinctes, plutôt que d'essayer de faire cette séparation entre les parties Majeure / Mineure (rupture) et Build / Révision (non cassante) de AssemblyVersion.
En bout de ligne: Réfléchissez bien lorsque vous modifiez votre AssemblyVersion
La morale est que si vous expédiez des assemblys auxquels d'autres développeurs vont faire référence, vous devez être extrêmement prudent lorsque vous modifiez (et ne modifiez pas) la version d'assembly de ces assemblys. Toute modification apportée à AssemblyVersion signifie que les développeurs d'applications devront soit recompiler la nouvelle version (pour mettre à jour ces entrées AssemblyRef), soit utiliser des redirections de liaison d'assembly pour remplacer manuellement la liaison.
- Ne modifiez pas AssemblyVersion pour une version de maintenance destinée à être rétrocompatible.
- Modifiez AssemblyVersion pour une version dont vous savez qu'elle comporte des modifications importantes.
Jetez un coup d'œil aux attributs de version sur 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")]
Notez que c'est le AssemblyFileVersion qui contient toutes les informations de maintenance intéressantes (c'est la partie Révision de cette version qui vous indique sur quel Service Pack vous êtes), tandis que le AssemblyVersion est fixé à un ancien ennuyeux 2.0.0.0. Toute modification apportée à AssemblyVersion forcerait chaque application .NET référençant mscorlib.dll à recompiler avec la nouvelle version!