El control de versiones de ensamblados en .NET puede ser una perspectiva confusa dado que actualmente hay al menos tres formas de especificar una versión para su ensamblaje.
Estos son los tres atributos principales de ensamblaje relacionados con la versión:
// 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")]
Por convención, las cuatro partes de la versión se denominan versión principal , versión secundaria , compilación y revisión .
El AssemblyFileVersion
objetivo es identificar de forma exclusiva una compilación del ensamblaje individual
Por lo general, configurará manualmente la versión de archivo de ensamblaje mayor y menor para reflejar la versión del ensamblaje, luego incrementará la compilación y / o revisión cada vez que su sistema de compilación compile el ensamblaje. AssemblyFileVersion debería permitirle identificar de forma exclusiva una compilación del ensamblado, de modo que pueda usarlo como punto de partida para depurar cualquier problema.
En mi proyecto actual, el servidor de compilación codifica el número de la lista de cambios de nuestro repositorio de control de código fuente en las partes de compilación y revisión de AssemblyFileVersion. Esto nos permite mapear directamente desde un ensamblado a su código fuente, para cualquier ensamblaje generado por el servidor de compilación (sin tener que usar etiquetas o ramas en el control de origen, o mantener manualmente cualquier registro de versiones publicadas).
Este número de versión se almacena en el recurso de versión Win32 y se puede ver al ver las páginas de propiedades del Explorador de Windows para el ensamblado.
Al CLR no le importa ni examina la versión del archivo de ensamblaje.
El AssemblyInformationalVersion
pretende representar la versión de la totalidad de su producto
AssemblyInformationalVersion está destinado a permitir versiones coherentes de todo el producto, que puede consistir en muchos ensamblajes que se versionan de forma independiente, tal vez con políticas de versiones diferentes y potencialmente desarrollados por equipos dispares.
“Por ejemplo, la versión 2.0 de un producto puede contener varios ensamblajes; uno de estos conjuntos está marcado como versión 1.0 ya que es un nuevo conjunto que no se envió en la versión 1.0 del mismo producto. Por lo general, configura las partes principales y secundarias de este número de versión para representar la versión pública de su producto. Luego, incrementa las partes de construcción y revisión cada vez que empaqueta un producto completo con todos sus ensamblajes ". - Jeffrey Richter, [CLR a través de C # (Segunda edición)] p. 57
Al CLR no le importa ni examina la versión de información de ensamblaje.
Esta AssemblyVersion
es la única versión que le interesa a CLR (pero le importa la totalidad AssemblyVersion
)
AssemblyVersion es utilizado por el CLR para enlazar a ensamblados fuertemente nombrados. Se almacena en la tabla de metadatos de manifiesto AssemblyDef del ensamblado creado y en la tabla AssemblyRef de cualquier ensamblado que haga referencia a ella.
Esto es muy importante, porque significa que cuando hace referencia a un ensamblado con un nombre fuerte, está estrechamente vinculado a una versión de ensamblaje específica de ese ensamblaje. La versión de ensamblaje completa debe ser una coincidencia exacta para que el enlace tenga éxito. Por ejemplo, si hace referencia a la versión 1.0.0.0 de un ensamblado con nombre en tiempo de compilación, pero solo la versión 1.0.0.1 de ese ensamblaje está disponible en tiempo de ejecución, ¡el enlace fallará! (A continuación, deberá solucionar este problema utilizando la redirección de enlace de ensamblaje ).
Confusión sobre si todo AssemblyVersion
tiene que coincidir. (Sí, lo hace)
Existe una pequeña confusión acerca de si la versión de ensamblaje completa tiene que coincidir exactamente para que se cargue un ensamblaje. Algunas personas tienen la falsa creencia de que solo las partes mayores y menores de la versión de la Asamblea tienen que coincidir para que la unión tenga éxito. Esta es una suposición sensata, sin embargo, en última instancia es incorrecta (a partir de .NET 3.5), y es trivial verificar esto para su versión del CLR. Simplemente ejecute este código de muestra .
En mi máquina, la segunda carga de ensamblaje falla, y las dos últimas líneas del registro de fusión dejan perfectamente claro por qué:
.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.
Creo que la fuente de esta confusión probablemente se deba a que Microsoft originalmente tenía la intención de ser un poco más indulgente con esta coincidencia estricta de la versión completa de la Asamblea, al coincidir solo en las partes de la versión Mayor y Menor:
"Al cargar un conjunto, el CLR encontrará automáticamente la última versión de servicio instalada que coincida con la versión principal / secundaria del conjunto que se solicita". - Jeffrey Richter, [CLR a través de C # (Segunda edición)] p. 56
Este era el comportamiento en Beta 1 del 1.0 CLR, sin embargo, esta característica se eliminó antes de la versión 1.0 y no ha logrado volver a aparecer en .NET 2.0:
“Nota: acabo de describir cómo debe pensar en los números de versión. Desafortunadamente, el CLR no trata los números de versión de esta manera. [En .NET 2.0], el CLR trata un número de versión como un valor opaco, y si un ensamblaje depende de la versión 1.2.3.4 de otro ensamblaje, el CLR intenta cargar la versión 1.2.3.4 solamente (a menos que exista una redirección vinculante ) Sin embargo,
Microsoft tiene planes de cambiar el cargador de CLR en una versión futura para que cargue la última compilación / revisión para una versión mayor / menor de un ensamblado. Por ejemplo, en una versión futura del CLR, si el cargador está tratando de encontrar la versión 1.2.3.4 de un ensamblado y existe la versión 1.2.5.0, el cargador recoge automáticamente la última versión de servicio. Este será un cambio muy bienvenido para el cargador de CLR. Por mi parte, no puedo esperar ". - Jeffrey Richter, [CLR a través de C # (Segunda edición)] p. 164 (El énfasis es mío)
Como este cambio aún no se ha implementado, creo que es seguro asumir que Microsoft ha retrocedido en este intento, y tal vez sea demasiado tarde para cambiarlo ahora. Traté de buscar en la web para averiguar qué sucedió con estos planes, pero no pude encontrar ninguna respuesta. Todavía quería llegar al fondo.
Así que le envié un correo electrónico a Jeff Richter y le pregunté directamente: supuse que si alguien sabía lo que sucedía, sería él.
Respondió dentro de las 12 horas, un sábado por la mañana, no menos, y aclaró que el cargador .NET 1.0 Beta 1 implementó este mecanismo de 'avance automático' para recoger la última compilación y revisión disponibles de un ensamblaje, pero este comportamiento fue revertido antes de .NET 1.0 enviado. Más tarde se intentó revivir esto, pero no llegó antes de que se enviara el CLR 2.0. Luego vino Silverlight, que tuvo prioridad para el equipo CLR, por lo que esta funcionalidad se retrasó aún más. Mientras tanto, la mayoría de las personas que vivían en los días de CLR 1.0 Beta 1 desde entonces se han mudado, por lo que es poco probable que esto vea la luz del día, a pesar de todo el arduo trabajo que ya se ha dedicado.
Parece que el comportamiento actual está aquí para quedarse.
También vale la pena señalar de mi discusión con Jeff que AssemblyFileVersion solo se agregó después de la eliminación del mecanismo de 'avance automático', porque después de 1.0 Beta 1, cualquier cambio en la AssemblyVersion fue un cambio importante para sus clientes, entonces hubo ningún lugar para almacenar de forma segura su número de compilación. AssemblyFileVersion es ese refugio seguro, ya que el CLR nunca lo examina automáticamente. Tal vez sea más claro de esa manera, tener dos números de versión separados, con significados separados, en lugar de tratar de hacer esa separación entre las partes Mayor / Menor (rompiendo) y Construir / Revisar (sin romper) de la Versión de la Asamblea.
El resultado final: piense detenidamente cuando cambie su AssemblyVersion
La moraleja es que si está enviando ensamblajes a los que otros desarrolladores harán referencia, debe tener mucho cuidado cuando cambie (y no) la versión de ensamblaje de esos ensamblajes. Cualquier cambio en la versión de ensamblaje significará que los desarrolladores de aplicaciones tendrán que volver a compilar con la nueva versión (para actualizar esas entradas de AssemblyRef) o usar redireccionamientos de enlace de ensamblaje para anular manualmente el enlace.
- No cambie la versión de ensamblaje por una versión de servicio diseñada para ser compatible con versiones anteriores.
- No cambiar el AssemblyVersion para un lanzamiento que se conoce tiene cambios de ruptura.
Simplemente eche otro vistazo a los atributos de la versión en 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")]
Tenga en cuenta que es el AssemblyFileVersion el que contiene toda la información de servicio interesante (es la parte de Revisión de esta versión que le dice en qué Service Pack está), mientras que el AssemblyVersion está arreglado en un viejo 2.0.0.0 aburrido. ¡Cualquier cambio en la versión de la Asamblea obligaría a cada aplicación .NET que haga referencia a mscorlib.dll a volver a compilar con la nueva versión!