我可以让我的程序集引用任何版本的另一个程序集吗?

时间:2014-06-04 21:56:05

标签: c# .net .net-3.5 c#-5.0

  • 我正在开发一个类库(MyClassLibrary)。
  • 我依赖第三方类库(ThirdPartyClassLibrary)。
  • 我需要使用与我的用户相同版本的ThirdPartyClassLibrary。例如,如果我在ThirdPartyClassLibrary中设置静态值,则用户需要查看该更改。
  • 我班级的用户可能依赖于ThirdPartyClassLibrary的4个不同版本中的任何一个。
  • ThirdPartyClassLibrary很大,我不想用我的软件分发它。
  • 我已经反映了ThirdPartyClassLibrary的所有4个版本,并验证了我将使用它们做的事情在所有版本中是兼容的(接口是相同的,方法签名是相同的等等)。
  • 我需要调用ThirdPartyClassLibrary才能获得高效率!每次我需要打电话时,我都无法反思所有事情。
  • MyClassLibrary将在运行时加载,因此我不能指望用户搞乱程序集绑定重定向或其他开发时设置(或任何设置,我的用户都无法做任何事情)。< / LI>
  • 我想从代码的编译时检查中受益,所以理想情况下根本没有反射。

如何编写MyClassLibrary,以便在加载到流程中时,无论用户加载了哪个版本的ThirdPartyClassLibrary,一切都能正常工作?

2 个答案:

答案 0 :(得分:11)

一种解决方法是在运行时使用AppDomain.AssemblyResolve 事件。只要程序集的分辨率失败,就会触发此消息。您可以使用它来将不同版本的程序集加载到CLR尝试加载的版本。

我在GitHub上添加了一个非常简单的演示:

https://github.com/danmalcolm/AssemblyResolutionDemo

设置如下:

  • 主应用程序App.exe直接引用程序集ThirdPartyLibrary.dll版本2.0.0.0。

  • 它还引用了MyLibrary,它引用了旧版本的ThirdPartyLibrary版本1.0.0.0。

  • 当版本1.0.0.0无法加载时,AppDomain.AssemblyResolve事件用于重定向到应用程序使用的版本

AssemblyResolve的处理如下:

public static void Initialise()
{
    AppDomain.CurrentDomain.AssemblyResolve += ResolveThirdPartyLibrary;
}

private static Assembly ResolveThirdPartyLibrary(object sender, ResolveEventArgs args)
{
    // Check that CLR is loading the version of ThirdPartyLibrary referenced by MyLibrary
    if (args.Name.Equals("ThirdPartyLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=fbcbfac3e44fefed"))
    {
        try
        {
            // Load from application's base directory. Alternative logic might be needed if you need to 
            // load from GAC etc. However, note that calling certain overloads of Assembly.Load will result
            // in the AssemblyResolve event from firing recursively - see recommendations in
            // http://msdn.microsoft.com/en-us/library/ff527268.aspx for further info
            var assembly = Assembly.LoadFrom("ThirdPartyLibrary.dll");
            return assembly;
        }
        catch (Exception exception)
        {
            Console.WriteLine(exception);
        }
    }
    return null;
}

我们需要在加载ThirdPartyLibrary之前绑定到事件,因此需要显式的Initialise方法。

另请注意,事件仅在程序集的分辨率失败时触发。如果MyClassLibrary(1.0.0.0)引用的ThirdPartyLibrary版本在GAC中可用,那么它将成功加载并且AssemblyResolve不会触发。然后会有2个不同的版本在使用。

我在这里证明这个机制可以使用,我并不是说它是个好主意。根据您的应用运行环境以及如何设置/安装/维护等,您需要考虑以下几点。

答案 1 :(得分:7)

不,你不能通过引用ThirdPartyClassLibrary来构建MyClassLibrary,其方式是&#34;只需使用运行时可用的任何版本的ThirdPartyClassLibrary.dll&#34;。

构建库时,任何引用的程序集的版本号都包含在程序集清单中。针对程序集运行ILDASM工具会显示如下内容:

...
.assembly extern ThirdPartyClassLibrary
{
  ...
  .ver 1:0:0:0
}
...

指定了ThirdPartyClassLibrary的名称和版本。在运行时,CLR将在第一次运行引用它的MyClassLibrary.dll中的指令时尝试加载ThirdPartyClassLibrary.dll。它将专门针对ThirdPartyClassLibrary.dll的1.0.0.0版本(如果它是一个强名称的程序集,也需要匹配的公钥)。

这里简要概述了CLR在运行时如何定位和绑定到程序集(http://msdn.microsoft.com/en-us/library/yx7xezcf(v=vs.110).aspx处的完整详细信息):

第1步 - 通过检查配置文件确定正确的程序集版本 - 我们将在下面回到此处,但就目前而言,如果您不这样做,则CLR将尝试加载引用程序集中指定的确切版本,因此它将查找版本1.0.0.0。

步骤2 - 检查程序集名称是否已绑定到之前,如果是,则使用先前加载的程序集。请注意&#34;程序集名称&#34;在此上下文中包含名称​​和版本,公钥令牌等,而不仅仅是dll文件的名称。

第3步 - 检查全局程序集缓存GAC(仅限强名称程序集)

第4步 - 通过代码库或探测找到程序集 - 本质上CLR在不同的地方查找以尝试在某处找到(特定版本)AssemblyB.dll。如果找不到特定版本,则会出错。它不会自动回退到早期版本或更高版本。

不幸的是,这意味着事情不会被工作&#34;并支持你上面描述的内容。如果引用MyClassLibrary的应用程序引用了ThirdPartyClassLibrary的更高版本(2.0.0.0),则在解析MyClassLibrary对ThirdPartyClassLibrary的引用时可能会发生一些不好的事情:

  • CLR无法找到程序集A使用的AssemblyB版本1.0.0.0并发生错误
  • 版本1.0.0.0恰好安装在GAC中并成功加载。虽然应用程序中的代码使用的是ThirdPartyClassLibrary版本2.0.0.0,但您的库正在使用ThirdPartyClassLibrary版本1.0.0.0。

您可以做的一件事是使用您的库配置应用程序,以便CLR将对不同版本的ThirdPartyClassLibrary.dll的引用统一为单个版本。这让我们回到上面概述的程序集绑定过程的第1步 - 我们实际上更改了CLR正在寻找的ThirdPartyClassLibrary的版本。

绑定重定向(http://msdn.microsoft.com/en-us/library/twy1dw1e.aspx)旨在将对程序集的不同版本的引用引导到单个版本。这些通常在应用程序的配置文件(Web.config,MyApp.exe.config)中定义,但也可以在机器级别(machine.config)全局定义。

以下是将所有早期版本的ThirdPartyClassLibrary.dll重定向到版本2.0.0.0的绑定重定向示例:

<configuration>

  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="AssemblyB" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>

</configuration>

请注意,这可以是automatically handled by Visual Studio 2013,它可以检测引用不同版本的程序集的情况,并为您添加绑定重定向。 NuGet包管理器控制台中还提供了Add-BindingRedirect命令。

绑定重定向是一种可能的解决方案,在某些情况下可能是一种实用的选择。但是,他们也可能会混淆您图书馆的用户。如果可行,您应该考虑分发不同版本的库,这些版本是根据第三方库的不同版本构建的。