我正在编写一个需要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中,开发人员可以控制目标系统(我也没有)。
答案 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失败,它将递归调用事件处理程序。