为什么我需要一个AssemblyResolve处理程序来处理已经加载的程序集?

10

我有两个程序集: AppAddOnApp 引用了 AddOn,但是 CopyLocal 被设置为 false,因为 AddOn 将由 App 动态加载。

这是 AddOn 中的代码:

namespace AddOn
{
    public class AddOnClass
    {
        public static void DoAddOnStuff()
        {
            Console.WriteLine("AddOn is doing stuff.");
        }
    }
}

这里是App的代码:

class Program
{
    static void Main(string[] args)
    {
        Assembly.LoadFrom(@"..\..\..\AddOn\bin\Debug\AddOn.dll");

        // Without this event handler, we get a FileNotFoundException.
        // AppDomain.CurrentDomain.AssemblyResolve += (sender, e) =>
        // {
        //     return AppDomain.CurrentDomain.GetAssemblies()
        //                     .FirstOrDefault(a => a.FullName == e.Name);
        //};

        CallAddOn();
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static void CallAddOn()
    {
        AddOnClass.DoAddOnStuff();
    }
}
我不理解的是,为什么在Main()中注释掉AssemblyResolve处理程序后代码就不能正常工作。在Visual Studio中运行时,调试器会在CallAddOn()处中断,并显示FileNotFoundException的错误信息。为什么会出现这种情况呢?该程序集已经被加载了,并且它与App引用的程序集完全相同(即磁盘上的同一文件)。
我感觉我没有正确理解某些基本概念。尽管已经注释掉了AssemblyResolve处理程序,但它似乎仍然可以正常工作,但它似乎像是一个黑客技巧,我不明白为什么需要它,因为它似乎只是做了一些微不足道的事情。

1
你做错了。当你允许在程序中使用插件时,你并不知道插件中的类的类型,因为它是由另一个程序员编写的。所以你应该对LoadFrom()方法的返回值非常感兴趣,例如你可以使用Assembly.GetType()方法来发现一个类型。 - Hans Passant
这是一个插件,它引用了另一个插件,因为它依赖于那个功能。 - RobSiklos
这不是一个插件,而只是一个普通的依赖程序集。您始终使用Copy Local = True来确保CLR可以自动找到程序集,而无需您的帮助。这是默认设置。 - Hans Passant
问题在于主机已经加载了AddOn1,而AddOn2希望利用这一点,而无需包含AddOn1本身(由于许可证原因,它无法执行此操作)。 - RobSiklos
这是通往DLL地狱的非常短的路径。这些类型的许可限制通常旨在阻止您像那样自残。它们不仅禁止添加引用,还禁止在DLL中使用代码。最好请公司的法律顾问审查许可条款,否则可能会惹上麻烦。 - Hans Passant
@HansPassant:收到,但我不确定允许我想要的最好的方法是什么。我在这里发布了一个新问题:http://stackoverflow.com/q/20221340/270348 - RobSiklos
1个回答

10
这是因为存在多个程序集加载上下文。程序集被加载到的上下文会影响它的使用方式。当一个程序集通过默认探测机制由运行时加载时,它被放置在所谓的“Load”上下文中。这是在通过Assembly.Load加载程序集时使用的上下文。您使用了LoadFrom来加载程序集,它使用自己的上下文。探测不会检查LoadFrom上下文,并且文件不在探测路径中,因此您需要为运行时解决它。然而,这并不对称。如果一个程序集在Load上下文中被加载,LoadFrom将首先从那里加载它(假设标识相同。对于未签名的程序集,路径是标识的一部分)。我会指出,还有更多的上下文,包括ReflectionOnlyLoadReflectionOnlyLoadFromLoadFile在没有上下文的情况下加载程序集,即必须手动加载所有依赖项。
如果您希望程序集在加载上下文中被解析,但又存在于应用程序默认探测路径之外,则可以通过配置实现。可以使用程序集绑定重定向的 <codebase> 元素或 <probing> 元素的 privatePath 属性。

阅读this以获取更多信息。Suzanne Cook之前写了一些关于程序集加载和上下文的博客文章(参见这里, 这里, 和 这里)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接