Il controllo delle versioni degli assembly in .NET può essere una prospettiva confusa dato che al momento esistono almeno tre modi per specificare una versione per il proprio assembly.
Ecco i tre principali attributi di assemblaggio relativi alla versione:
// 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")]
Per convenzione, le quattro parti della versione sono indicati come la versione principale , la versione minore , Costruire , e di revisione .
L' AssemblyFileVersion
obiettivo è identificare in modo univoco una build del singolo assieme
In genere, imposti manualmente il file AssemblyFileVersion principale e secondario in modo che rifletta la versione dell'assembly, quindi aumenti la build e / o la revisione ogni volta che il sistema di compilazione compila l'assembly. AssemblyFileVersion dovrebbe consentire di identificare in modo univoco una build dell'assembly, in modo da poterlo utilizzare come punto di partenza per il debug di eventuali problemi.
Nel mio progetto attuale, il server di build codifica il numero dell'elenco modifiche dal nostro repository di controllo del codice sorgente nelle parti Build e Revision di AssemblyFileVersion. Questo ci consente di mappare direttamente da un assembly al suo codice sorgente, per qualsiasi assembly generato dal server di compilazione (senza dover utilizzare etichette o rami nel controllo del codice sorgente o conservare manualmente i record delle versioni rilasciate).
Questo numero di versione è archiviato nella risorsa versione Win32 e può essere visualizzato quando si visualizzano le pagine delle proprietà di Esplora risorse per l'assembly.
Al CLR non interessa né esamina AssemblyFileVersion.
L' AssemblyInformationalVersion
obiettivo è rappresentare la versione dell'intero prodotto
AssemblyInformationalVersion ha lo scopo di consentire un versioning coerente dell'intero prodotto, che può consistere in numerosi assembly con versione indipendente, forse con politiche di versioning diverse e potenzialmente sviluppati da team diversi.
“Ad esempio, la versione 2.0 di un prodotto potrebbe contenere diversi assiemi; uno di questi assembly è contrassegnato come versione 1.0 poiché è un nuovo assembly che non è stato fornito nella versione 1.0 dello stesso prodotto. In genere, si impostano le parti principali e secondarie di questo numero di versione per rappresentare la versione pubblica del prodotto. Quindi si aumentano le parti di costruzione e revisione ogni volta che si confeziona un prodotto completo con tutti i suoi assiemi. ” - Jeffrey Richter, [CLR via C # (Seconda Edizione)] p. 57
Il CLR non si preoccupa né esamina la AssemblyInformationalVersion.
L' AssemblyVersion
è l'unica versione cure CLR circa (ma si preoccupa per l'intero AssemblyVersion
)
AssemblyVersion viene utilizzato da CLR per associarsi a assembly con un nome sicuro. È archiviato nella tabella dei metadati manifest di AssemblyDef dell'assembly creato e nella tabella AssemblyRef di qualsiasi assembly che fa riferimento a esso.
Questo è molto importante, perché significa che quando si fa riferimento a un assembly con un nome sicuro, si è strettamente legati a una specifica AssemblyVersion di tale assembly. L'intera AssemblyVersion deve essere una corrispondenza esatta affinché l'associazione abbia esito positivo. Ad esempio, se si fa riferimento alla versione 1.0.0.0 di un assembly con nome sicuro in fase di compilazione, ma solo la versione 1.0.0.1 di tale assembly è disponibile in fase di esecuzione, l'associazione non riuscirà! (Dovrai quindi ovviare a questo usando il reindirizzamento del binding dell'assieme .)
Confusione sul fatto che l'intero AssemblyVersion
debba corrispondere. (Sì, lo fa.)
C'è un po 'di confusione sul fatto che l'intera AssemblyVersion debba essere una corrispondenza esatta per poter caricare un assembly. Alcune persone hanno la falsa convinzione che solo le parti principali e minori dell'AssembleaVersione devono abbinare per avere successo. Questo è un presupposto ragionevole, tuttavia alla fine non è corretto (a partire da .NET 3.5) ed è banale verificarlo per la propria versione del CLR. Basta eseguire questo codice di esempio .
Sulla mia macchina il secondo carico di assemblaggio fallisce e le ultime due righe del registro di fusione chiariscono perfettamente perché:
.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.
Penso che la fonte di questa confusione sia probabilmente perché Microsoft originariamente intendeva essere un po 'più indulgente in questo stretto abbinamento di AssemblyVersion completo, abbinando solo le parti della versione Major e Minor:
"Quando si carica un assieme, il CLR troverà automaticamente l'ultima versione di manutenzione installata che corrisponde alla versione principale / secondaria dell'assieme richiesto." - Jeffrey Richter, [CLR via C # (Seconda Edizione)] p. 56
Questo era il comportamento in Beta 1 del CLR 1.0, tuttavia questa funzionalità è stata rimossa prima della versione 1.0 e non è riuscita a risalire in .NET 2.0:
“Nota: ho appena descritto come dovresti pensare ai numeri di versione. Sfortunatamente, il CLR non tratta i numeri di versione in questo modo. [In .NET 2.0], il CLR considera un numero di versione come un valore opaco e se un assembly dipende dalla versione 1.2.3.4 di un altro assembly, il CLR tenta di caricare solo la versione 1.2.3.4 (a meno che non sia presente un reindirizzamento vincolante ). Tuttavia,
Microsoft ha in programma di modificare il caricatore del CLR in una versione futura in modo che carichi l'ultimo build / revisione per una determinata versione principale / secondaria di un assembly . Ad esempio, su una versione futura del CLR, se il caricatore sta cercando di trovare la versione 1.2.3.4 di un assieme ed esiste la versione 1.2.5.0, il caricatore con preleva automaticamente l'ultima versione di manutenzione. Questa sarà una modifica molto gradita al caricatore del CLR - io per uno non vedo l'ora ”. - Jeffrey Richter, [CLR via C # (Seconda Edizione)] p. 164 (Enfasi mia)
Poiché questo cambiamento non è ancora stato implementato, penso che sia sicuro supporre che Microsoft abbia rinviato questo intento, ed è forse troppo tardi per cambiarlo ora. Ho provato a cercare sul Web per scoprire cosa è successo con questi piani, ma non sono riuscito a trovare alcuna risposta. Volevo ancora arrivare fino in fondo.
Così ho mandato un'e-mail a Jeff Richter e gli ho chiesto direttamente: ho pensato che se qualcuno avesse saputo cosa sarebbe successo, sarebbe stato lui.
Ha risposto entro 12 ore, un sabato mattina non meno, e ha chiarito che il caricatore .NET 1.0 Beta 1 ha implementato questo meccanismo di "roll-forward automatico" per raccogliere l'ultima build disponibile e revisione di un assembly, ma questo comportamento era ripristinato prima della spedizione di .NET 1.0. In seguito è stato progettato per rilanciare questo, ma non è arrivato prima della spedizione del CLR 2.0. Poi è arrivata Silverlight, che ha avuto la priorità per il team CLR, quindi questa funzionalità è stata ulteriormente ritardata. Nel frattempo, la maggior parte delle persone che erano in giro ai tempi di CLR 1.0 Beta 1 sono passate da allora, quindi è improbabile che questo vedrà la luce del giorno, nonostante tutto il duro lavoro che era già stato messo dentro.
Il comportamento attuale, a quanto pare, è qui per rimanere.
Vale anche la pena notare dalla mia discussione con Jeff che AssemblyFileVersion è stato aggiunto solo dopo la rimozione del meccanismo di "roll-forward automatico" - perché dopo 1.0 Beta 1, qualsiasi modifica a AssemblyVersion è stata una svolta per i tuoi clienti, quindi da nessuna parte per memorizzare in sicurezza il numero di build. AssemblyFileVersion è un rifugio sicuro, poiché non viene mai esaminato automaticamente dal CLR. Forse è più chiaro in quel modo, con due numeri di versione separati, con significati separati, piuttosto che cercare di fare quella separazione tra le parti Major / Minor (break) e Build / Revision (non break) di AssemblyVersion.
La linea di fondo: pensa attentamente quando cambi il tuo AssemblyVersion
La morale è che se stai spedendo assemblee a cui faranno riferimento altri sviluppatori, devi fare molta attenzione quando cambi (e non cambi) la AssemblyVersion di quegli assemblaggi. Eventuali modifiche a AssemblyVersion significheranno che gli sviluppatori di applicazioni dovranno ricompilare la nuova versione (per aggiornare quelle voci di AssemblyRef) o utilizzare i reindirizzamenti dell'associazione dell'assieme per sostituire manualmente l'associazione.
- Non modificare AssemblyVersion per una versione di manutenzione che deve essere compatibile con le versioni precedenti.
- Non cambiare l'AssemblyVersion per un rilascio che si sa ha cambiamenti di rottura.
Dai un'occhiata agli attributi della versione su 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")]
Nota che è AssemblyFileVersion che contiene tutte le informazioni di manutenzione interessanti (è la parte Revision di questa versione che ti dice su quale Service Pack stai usando), nel frattempo AssemblyVersion è riparato su un vecchio noioso 2.0.0.0. Qualsiasi modifica a AssemblyVersion costringerebbe ogni applicazione .NET che fa riferimento a mscorlib.dll a ricompilarsi con la nuova versione!