为什么我不能将在运行时动态加载的程序集共享到同一个加载上下文中?

时间:2009-04-24 20:22:05

标签: .net reflection assemblies

我有一个在构建插件架构时并不罕见的问题。

  1. 程序集A是核心代码 - 框架。

  2. 程序集B是该代码的插件,希望在运行时动态加载并使代码可供程序集A使用。

  3. 在Visual Studio中,项目B(生成程序集B)引用了项目A(生成程序集A),因此它可以引用项目A中的类型并编译得很好。两个项目都编译没有错误并生成DLL。

    但是,在运行时,我会得到像这样的强制转换:

    'MyType' cannot be converted to type 'MyType'
    

    我已经明白,在一个项目中定义并在另一个项目中引用的类型在运行时被视为不同的类型,即使它们在编译时解析得很好。

    但是,我认为可以通过将程序集B加载到正确的上下文中来解决这个问题。所以我从“Assembly.LoadFrom”切换到“Assembly.Load”并将组件B放在探测路径中。这个想法是它会加载到与A组相同的环境中,它们将是一个幸福的大家庭。

    我下载了Fusion日志查看器并观看了程序集加载。程序集A和程序集B都加载如下:

      

    程序集在默认的加载上下文中加载。

    所以,我确保它们都加载到相同的加载上下文

    但是,我无法在两个装配体之间共享类型。当我尝试将一个对象(其类型在程序集A中定义)作为参数从程序集A中的代码传递给程序集B中对象的方法时,我得到了转换错误。

    总结:

    1. MyType在程序集A中定义

    2. 从程序集A,我使用“Assembly.Load”将程序集B在运行时加载到相同的加载上下文中

    3. 从程序集A中,我使用Reflection在程序集B中的类上调用静态方法。我将此方法传递给MyType的一个对象作为参数(这是预期的,并且编译得很好)。

    4. 失败:'MyType'无法转换为'MyType'类型

    5. 这是我用来调用方法的代码,如果这很重要的话。此代码在程序集A中执行:

      TypeFromAssemblyB.GetMethod("MyMethod").Invoke(null, new object[] { ObjectOfTypeDefinedInAssemblyA });
      

5 个答案:

答案 0 :(得分:2)

我确信这应该像你描述的那样工作,所以我写了一个快速而又肮脏的例子。我的入口程序集名为PluginTest,包含以下类:

namespace PluginTest
{
    public class MyType
    {
        public string Data { get; set; }
    }
}

我的插件程序集名为Plugin。它引用了Visual Studio中的PluginTest项目,只包含这个类:

using System;
using PluginTest;

namespace Plugin
{
    public class Class1
    {
        public static void ShowData(MyType input)
        {
            Console.WriteLine(input.Data);
        }
    }
}

最后回到PluginTest(可执行文件)我在Main中有这个代码:

static void Main(string[] args)
        {
            Assembly pluginAssembly = Assembly.LoadFrom("Plugin.dll");

            Type pluginType = pluginAssembly.GetType("Plugin.Class1");

            MethodInfo mi = pluginType.GetMethod("ShowData");

            mi.Invoke(null, new object[]
            {
                new MyType {Data = "Hello World"}
            });

            Console.ReadLine();

        }

正如预期的那样,会弹出一个控制台窗口并向其写入“Hello World”。所以,原则上,你想要做的事情应该工作......

(在预览中我喜欢Jonathan的想法,它可能是版本不匹配 - 对整个解决方案进行干净的构建,并确保将所有dll复制到正确的文件夹中)

答案 1 :(得分:0)

您是否有可能出现版本不匹配问题?如果程序集B引用程序集A的v1.0.x.y,但程序集A的加载版本为v1.0.w.z,则这些类型不兼容。将它们加载到反射器(或类似工具)中应该允许您验证每个组件的版本,以及从B到A的引用版本。

我偶尔遇到这种情况,我部署了一个使用自动版本号的应用程序,然后执行重建(这会增加两个程序集上的版本),并且只重新部署其中一个新库。当然,在我的情况下,我没有使用这种动态加载或反射来在它们之间进行调用,因此在这里你可能会遇到一个更复杂的问题。

答案 2 :(得分:0)

这只是一个快速反应,所以可能会有一些漏洞。

我们一直在编写插件。在你描述的invokation中,我希望ObjectOfTypeDefinedInAssemblyA是你在程序集B中声明的,没有命名冲突。它似乎严重违反了OOP的松耦合性质。

例如,我们的业务应用程序中的一个插件处理特殊类型的局间消息传递。我不希望程序集A知道/定义封装消息的对象。我希望可以调用程序集B来从程序集A中实例化该对象;然后,组件B会通过调用将其传回给A.

答案 3 :(得分:0)

可能是您的程序集被加载到“默认加载上下文”和其他一些上下文(或者没有加载)。 Fusion日志中是否还有其他条目表明发生了这种情况?

MSDN上的

Avoid Loading an Assembly into Multiple Contexts提供了更多详细信息。

答案 4 :(得分:0)

描述的技术here将允许您强制将程序集加载到默认的加载上下文中。