是否可以用“弱”引用替换对具有强名称的程序集的引用?

时间:2011-07-29 14:51:06

标签: .net reference assemblies strongname

我正在编写一个需要SQL Server SMO库的.NET工具。我不在乎它是Server 2005(9.0),2008(10.0)还是2008 R2(可能是10.5,没有检查)的版本。 SMO库与SQL Server一起安装,因此我可以放心地假设在安装了SQL Server的任何系统上,也可以使用某些版本的SMO库。

不幸的是,SMO库名称很强:如果我在项目中添加对SMO 9.0的引用,如果客户系统上只有SMO 10.0,它将失败(FileNotFoundException),反之亦然。

有没有办法告诉编译器任何版本的库对我来说没问题?或者我真的需要分发3个相同版本的工具,每个编译成SMO的不同版本?


免责声明:我知道可以重新分发SMO库(以及SMO库所需的库)。但是(a)一个超薄的100KB独立EXE和(b)安装了大量先决条件的完整安装包之间存在很大差异。

免责声明2:我知道以下重复:

但是,所提供的解决方案并不合适。在问题1中,开发人员可以控制引用的DLL(我没有);在问题2中,开发人员可以控制目标系统(我也没有)。

3 个答案:

答案 0 :(得分:6)

您可以使用Assembly Binding Redirection

例如:

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

<强>更新

我从你的评论中看到,我们必须反过来思考一下。

  
    

一种非常有前途的方法,但不幸的是,它只是将对版本X的依赖性替换为对版本Y的(强)依赖性。我仍然依赖于某个特定版本。

  

我做了一些实验,我编译了一个版本的程序集:4.0.0.0,但是想确保它会加载该版本,加上一些选定的旧版本。通过这种方式,您不依赖于任何单个版本,而是依赖于您配置的任何版本。

如果系统中存在以下任何版本,则以下内容将确保将加载VersionedAssembly: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>

答案 1 :(得分:6)

据我所知,不可能删除对精确版本的依赖。这是强名称存在的原因之一 - 避免版本不匹配。程序集的内部甚至公共接口可以在版本之间进行更改,您可以发现新版本与旧版本不向后兼容。因为.NET会查找编译期间使用的版本,以确保应用程序正常工作。

如果第三方决定他们的新版本是向后兼容的,并且他们将程序集部署到GAC,他们可以添加publisher policy,这将自动重定向。

如果您决定强制加载另一个程序集,则可以使用@chibacity提到的方法或为AppDomain.CurrentDomain.AssemblyResolve实现处理程序。当.NET无法找到引用的程序集时,会触发此事件,您可以通过调用Assembly.LoadFrom来实现自己的逻辑来查找并加载它。在这种情况下,完全取决于您加载的版本。

答案 2 :(得分:0)

基于Ladislav's suggestion覆盖AssemblyResolve的情况,我提出了以下解决方案:

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

注意:直接在AssemblyResolve事件处理程序中使用Assembly.Load并不是一个好主意,因为如果Load失败,它将递归调用事件处理程序。