我的程序集可以引用其他程序集的任何版本吗? [英] Can I have my assembly reference any version of another assembly?

查看:93
本文介绍了我的程序集可以引用其他程序集的任何版本吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述


  • 我正在开发一个类库( MyClassLibrary )。

  • 我依赖第三方类库( ThirdPartyClassLibrary )。

  • 我需要使用相同版本的 ThirdPartyClassLibrary 作为我的用户。例如,如果我在 ThirdPartyClassLibrary 中设置了静态值,则用户需要查看该更改。

  • 我班的用户可能取决于 ThirdPartyClassLibrary 的四个不同版本中的任何一个。

  • ThirdPartyClassLibrary 我不想随软件一起分发。

  • 我已经考虑了 ThirdPartyClassLibrary 的所有4个版本,并验证了我所做的事情将使用它们在所有版本中兼容(接口是相同的,方法签名是相同的,等等)。

  • 我需要调用 ThirdPartyClassLibrary 表现出色!我无法在每次需要调用某些东西时都进行反思。

  • MyClassLibrary 将在运行时加载,因此我可以希望用户不会使用程序集绑定重定向或其他开发时设置(或者根本没有任何设置,我的用户无法执行任何操作)。

  • 我希望从compile-



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

解决方案

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



我在GitHub此处添加了一个非常简单的演示: / p>

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 Initialize( )
{
AppDomain.CurrentDomain.AssemblyResolve + = ResolveThirdPartyLibrary;
}

私有静态程序集ResolveThirdPartyLibrary(对象发送者,ResolveEventArgs args)
{
//检查CLR是否正在加载MyLibrary $ b $引用的ThirdPartyLibrary版本b if(args.Name.Equals( ThirdPartyLibrary,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = fbcbfac3e44fefed)))
{
try
{
//加载从应用程序的基本目录。如果需要从GAC等中加载
//,则可能需要其他逻辑。但是,请注意,调用Assembly.Load的某些重载将导致Assemblybsolve事件中的
//递归触发-请参见中的建议
// http://msdn.microsoft.com/zh-cn/library/ff527268.aspx了解更多信息
var assembly = Assembly.LoadFrom( ThirdPartyLibrary.dll);
返回程序集;
}
catch(异常例外)
{
Console.WriteLine(exception);
}
}
返回null;
}

我们需要在加载ThirdPartyLibrary之前绑定到事件,因此需要进行显式初始化方法。



还请注意,只有在程序集解析失败时才触发该事件。如果GAC中提供了MyClassLibrary(1.0.0.0)引用的ThirdPartyLibrary版本,则它将成功加载,并且不会触发AssemblyResolve。然后将有2个不同的版本在使用。



我在这里说明可以使用这种机制,我并不是说这是一个好主意。根据您应用的运行环境以及其设置/安装/维护方式等,您需要考虑几件事。


  • I am developing a class library (MyClassLibrary).
  • I depend on a third party class library (ThirdPartyClassLibrary).
  • I need to use the same version of ThirdPartyClassLibrary as my users. e.g., if I set a static value in ThirdPartyClassLibrary the user needs to see that change.
  • Users of my class may be depending on any one of 4 different versions of ThirdPartyClassLibrary.
  • ThirdPartyClassLibrary is large, I do not want to distribute it with my software.
  • I have reflected on all 4 versions of ThirdPartyClassLibrary and validated that the things I will be doing with them are compatible across all versions (interfaces are the same, methods signatures are the same, etc.).
  • I need calls into ThirdPartyClassLibrary to be performant! I can't reflect on everything every time I need to call something.
  • MyClassLibrary will be loaded at runtime, so I can't expect users to mess with assembly binding redirects or other develop-time settings (or any settings at all, my users are resistant to doing anything).
  • I would like to benefit from compile-time checking of my code, so ideally no reflection at all.

How can I write MyClassLibrary such that when it is loaded into the process everything works correctly with whichever version of ThirdPartyClassLibrary the user has loaded?

解决方案

One workaround would be to use the AppDomain.AssemblyResolve event at runtime. This fires whenever the resolution of an assembly fails. You can use this to load a different version of an assembly to that which the CLR is trying to load.

I've added a very simple demo on GitHub here:

https://github.com/danmalcolm/AssemblyResolutionDemo

This is set up as follows:

  • The main application App.exe directly references assembly ThirdPartyLibrary.dll version 2.0.0.0.

  • It also references MyLibrary, which references an older version of ThirdPartyLibrary version 1.0.0.0.

  • The AppDomain.AssemblyResolve event is used to redirect to the version used by the application when version 1.0.0.0 fails to load

AssemblyResolve is handled as follows:

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

We need to bind to the event before ThirdPartyLibrary is loaded, hence the explicit Initialise method.

Note also that the event only fires when the resolution of an assembly fails. If the version of ThirdPartyLibrary referenced by MyClassLibrary (1.0.0.0) were available in the GAC, then it would be loaded successfully and AssemblyResolve wouldn't fire. There would then be 2 different versions in use.

I'm demonstrating here that this mechanism could be used, I'm not saying it's a good idea. There are several things you'd need to take into account based on the environment in which your app is running and how it is set-up / installed / maintained etc.

这篇关于我的程序集可以引用其他程序集的任何版本吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆