Is it possible to replace a reference to a strongly-named assembly with a “weak” reference?

▼魔方 西西 提交于 2019-11-27 14:21:45

As I know it is not possible to remove the dependency on exact version. That is one of reasons why strong names exist - to avoid version mismatch. Internals or even public interfaces of the assembly can change among version and you can find that new version is not backward compatible with the old one. Because of that .NET looks for version used during compilation to make sure that application works correctly.

If third party decides that their new version is backward compatible and if they deploy assembly to GAC they can add publisher policy which will do redirect automatically.

If you decide that you want to force loading another assembly you can use the approach mentioned by @chibacity or implement handler for AppDomain.CurrentDomain.AssemblyResolve. This event fires when .NET is not able to find referenced assembly and you can implement your own logic to find it and load it by calling Assembly.LoadFrom. In such case it is completely up to you which version you load.

You can use Assembly Binding Redirection.

For example:

 <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>

Update

I see from your comment that we have to think about this in reverse a little.

A very promising approach, but, unfortunately, it merely replaces the dependency on version X with a (strong) dependency on version Y. I still have a dependency on one particular version.

I did some experiments where I compiled against a version of an assembly: 4.0.0.0, but wanted to make sure it would load that version, plus some selected older versions. In this way you are not dependent on any single version but against any of the versions you have configured.

The following will ensure that VersionedAssembly will be loaded if any of the following versions are on the system: 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>

Based on Ladislav's suggestion of overriding AssemblyResolve, I was able to come up with the following solution:

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

Note: Using Assembly.Load directly in the AssemblyResolve event handler is not a good idea, since it recursively calls the event handler if Load fails.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!