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

发布于 2024-11-27 06:21:02 字数 841 浏览 1 评论 0原文

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

I'm writing a .NET tool that requires the SQL Server SMO library. I don't care if it's the version from Server 2005 (9.0), 2008 (10.0) or 2008 R2 (probably 10.5, didn't check). The SMO library is installed together SQL Server, so I can safely assume that on any system with SQL Server installed, some version of the SMO library is available as well.

Unfortunately, the SMO libraries are strongly-named: If I add a reference to SMO 9.0 in my project, it will fail (FileNotFoundException) if only SMO 10.0 is present on the customer's system, and vice versa.

Is there some way to tell the compiler that any version of the library is fine for me? Or do I really have to distribute 3 identical versions of my tool, each compiled to a different version of the SMO?


Disclaimer: I do know that the SMO libraries (and the libraries required by the SMO libraries) can be redistributed. But there's a big difference between (a) one slim 100KB standalone EXE and (b) a full-blown setup package that installs a whole bunch of prerequisites.

Disclaimer 2: I am aware of the following duplicates:

The solutions provided do not fit, however. In question 1, the developer has control over the referenced DLL (which I do not); in question 2, the developer has control over the target systems (which I do not either).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

喜爱皱眉﹌ 2024-12-04 06:21:02

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

如果第三方确定其新版本向后兼容,并且将程序集部署到 GAC,则可以添加

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

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.

情未る 2024-12-04 06:21:02

您可以使用程序集绑定重定向

例如:

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

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>
陪你到最终 2024-12-04 06:21:02

根据Ladislav的建议重写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 失败,它会递归调用事件处理程序。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文