.net - with - ¿Es posible reemplazar una referencia a un ensamblaje con un nombre seguro por una referencia "débil"?
sign an assembly with a strong name (3)
Basándome en la sugerencia de Ladislav de anular la resolución de la resolución de la AssemblyResolve
, pude encontrar la siguiente solución:
Sub Main()
...
Dim assembly = GetSmoAssembly()
If assembly Is Nothing Then
'' no suitable Version of SMO found
...
Else
'' load correct assembly
Dim returnAssembly As ResolveEventHandler = Function() assembly
AddHandler AppDomain.CurrentDomain.AssemblyResolve, returnAssembly
TestSmo()
RemoveHandler AppDomain.CurrentDomain.AssemblyResolve, returnAssembly
End If
...
End Sub
Private Function GetSmoAssembly() As Assembly
Try
Return Assembly.Load("Microsoft.SqlServer.Smo, Version=9.0.242.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91")
Catch ex As FileNotFoundException
End Try
Try
Return Assembly.Load("Microsoft.SqlServer.Smo, Version=10.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91")
Catch ex As FileNotFoundException
End Try
Return Nothing
End Function
'' Needs to be in a separate method, see https://stackoverflow.com/q/6847765/87698
Private Sub TestSmo()
Dim srv As New Smo.Server()
End Sub
Nota: No es una buena idea usar Assembly.Ladar directamente en el controlador de eventos AssemblyResolve, ya que se llama de forma recurrente al controlador de eventos si falla la carga.
Estoy escribiendo una herramienta .NET que requiere la biblioteca SMO de SQL Server. No me importa si es la versión de Server 2005 (9.0), 2008 (10.0) o 2008 R2 (probablemente 10.5, no verifiqué). La biblioteca SMO se instala junto con SQL Server, por lo que puedo asumir con seguridad que, en cualquier sistema con SQL Server instalado, también hay disponible una versión de la biblioteca SMO.
Desafortunadamente, las bibliotecas de SMO tienen un nombre FileNotFoundException
: si agrego una referencia a SMO 9.0 en mi proyecto, fallará ( FileNotFoundException
) si solo está presente SMO 10.0 en el sistema del cliente, y viceversa.
¿Hay alguna manera de decirle al compilador que cualquier versión de la biblioteca está bien para mí? ¿O realmente tengo que distribuir 3 versiones idénticas de mi herramienta, cada una compilada a una versión diferente del SMO?
Descargo de responsabilidad: Sé que las bibliotecas de SMO (y las bibliotecas requeridas por las bibliotecas de SMO) se pueden redistribuir. Pero hay una gran diferencia entre (a) un EXE independiente delgado de 100KB y (b) un paquete de instalación completo que instala una gran cantidad de requisitos previos.
Descargo de responsabilidad 2: Soy consciente de los siguientes duplicados:
- c # - ¿puede hacer una referencia de ensamblaje "débil" a un ensamblado con nombre seguro?
- Necesita un ensamblaje de C # para hacer referencia a un ensamblaje con un nombre seguro sin holgura
Sin embargo, las soluciones provistas no encajan. En la pregunta 1, el desarrollador tiene control sobre la DLL referenciada (que yo no); en la pregunta 2, el desarrollador tiene control sobre los sistemas de destino (lo cual tampoco lo hago).
Como sé, no es posible eliminar la dependencia de la versión exacta. Esa es una de las razones por las que existen nombres fuertes, para evitar la falta de coincidencia de versiones. Los internos o incluso las interfaces públicas del ensamblaje pueden cambiar entre versiones y puede encontrar que la nueva versión no es compatible con la anterior. Debido a eso, .NET busca la versión utilizada durante la compilación para asegurarse de que la aplicación funciona correctamente.
Si un tercero decide que su nueva versión es compatible con versiones anteriores y si implementa el ensamblaje en GAC, puede agregar una política de editor que redirigirá automáticamente.
Si decide que desea forzar la carga de otro conjunto, puede utilizar el enfoque mencionado por @chibacity o el controlador de implementos para AppDomain.CurrentDomain.AssemblyResolve
. Este evento se activa cuando .NET no puede encontrar el ensamblaje al que se hace referencia y puede implementar su propia lógica para encontrarlo y cargarlo llamando a Assembly.LoadFrom
. En tal caso, depende completamente de usted qué versión cargue.
Puede utilizar la redirección de enlace de ensamblaje .
Por ejemplo:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Telerik.Web.UI" publicKeyToken="121fae78165ba" />
<bindingRedirect
oldVersion="2010.0.0.1"
newVersion="2011.1.315.40" />
</dependentAssembly>
</assemblyBinding>
</runtime>
Actualizar
Veo por su comentario que tenemos que pensar un poco al revés.
Un enfoque muy prometedor, pero, desafortunadamente, simplemente reemplaza la dependencia en la versión X con una dependencia (fuerte) en la versión Y. Todavía tengo una dependencia en una versión en particular.
Hice algunos experimentos en los que compilé contra una versión de un ensamblaje: 4.0.0.0, pero quería asegurarme de que cargaría esa versión, además de algunas versiones anteriores seleccionadas. De esta manera, no depende de ninguna versión individual, sino de cualquiera de las versiones que haya configurado.
Lo siguiente asegurará que VersionedAssembly
se cargará si alguna de las siguientes versiones está en el sistema: 4.0.0.0, 3.0.0.0, 2.0.0.0, 1.0.0.0.
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="VersionedAssembly" publicKeyToken="20d85e" />
<bindingRedirect oldVersion="4.0.0.0" newVersion="1.0.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="VersionedAssembly" publicKeyToken="20d84e" />
<bindingRedirect oldVersion="4.0.0.0" newVersion="2.0.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="VersionedAssembly" publicKeyToken="20d84e" />
<bindingRedirect oldVersion="4.0.0.0" newVersion="3.0.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>