【问题标题】:Is it possible to replace a reference to a strongly-named assembly with a "weak" reference?是否可以用“弱”引用替换对强名称程序集的引用?
【发布时间】:2011-10-16 00:59:40
【问题描述】:

我正在编写一个需要 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 中,开发人员可以控制目标系统(我也没有)。

【问题讨论】:

  • 有趣的问题。对这个主题一无所知,对依赖注入几乎一无所知,可以这样实现吗?我的意思是,不同版本的程序集是您打算注入的依赖项?只是一个想法..
  • 对不起,我的回答完全错误,具体版本是build-time only setting
  • @Smudge202 有趣的想法,但有一个绊脚石。 DI 将取决于可以作为注入基础的某种类型(例如接口)。鉴于此的第 3 方性质,该类型将存在于第 3 方库中 - 砰 - 你依赖于第 3 方库的特定版本。

标签: .net reference assemblies strongname


【解决方案1】:

基于覆盖AssemblyResolveLadislav's suggestion,我能够提出以下解决方案:

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 失败,它会递归调用事件处理程序。

【讨论】:

    【解决方案2】:

    据我所知,不可能消除对确切版本的依赖。这就是存在强名称的原因之一——以避免版本不匹配。程序集的内部结构甚至公共接口都可能在版本之间发生变化,您会发现新版本与旧版本不向后兼容。因此,.NET 会在编译期间查找使用的版本,以确保应用程序正常工作。

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

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

    【讨论】:

    • 脚注:如果未签名的程序集 A 通过对 B 的强名称引用依赖于已签名的 B v1,则通过处理 AssemblyResolve 事件,您可以使 A 使用 B v2 但仅当B v2 也已签名时。如果B v2 没有签名,A 会拒绝使用。
    • 注意“也签名”我的意思是相同的publicKeyToken。我猜这个想法是替换 DLL 必须来自同一作者。
    【解决方案3】:

    您可以使用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>
    

    更新

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

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

    我做了一些实验,针对程序集的一个版本进行编译: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>
    

    【讨论】:

    • 一种非常有前途的方法,但不幸的是,它只是用对版本 Y 的(强)依赖替换了对版本 X 的依赖。我仍然依赖于一个特定版本。
    • @Heinzi 是的,但您会将所有旧版本指向最新版本。您最终得到的是指向 Z 的 W、X、Y、Z 版本。但是,更重要的是这些版本是受支持和 测试 的版本。如果存在不同的版本,它甚至会与您实际编译的版本兼容吗?
    • @Heinzi 您还可以选择使用通配符样式的版本控制,例如oldVersion="0.0.0.0-65535.65535.65535.65535".
    • 很遗憾,我无法重现您的结果。我已经参考 v9 编译到应用程序。如果我在配置文件中添加重定向 9->10,它在安装了 10 的系统上工作。显然,仅使用找到的 first 匹配dependentAssembly 指令:如果我在 9->10 前面添加重定向 9->9,它only 在具有 9 的系统上工作已安装。
    • @Heinzi 我今晚会重新检查。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多