MEF:“无法加载一个或多个请求的类型。检索LoaderExceptions以获取更多信息”

时间:2010-10-26 03:42:51

标签: exception plugins mef loader

场景:我正在使用Managed Extensibility Framework在运行时根据在单独的dll中定义的接口契约加载插件(导出)。在我的Visual Studio解决方案中,我有3个不同的项目:主机应用程序,类库(定义接口 - “IPlugin”)和另一个实现接口的类库(导出 - “MyPlugin.dll”)。

主机在其自己的根目录中查找导出,因此在测试期间,我构建整个解决方案并将Plugin.dll从Plugin类库bin / release文件夹复制到主机的调试目录,以便主机的DirectoryCatalog可以找到它并能够将它添加到CompositionContainer。每次重建后都不会自动复制Plugin.dll,所以每次我对合同/实现进行更改时都会手动复制。

但是,有几次我运行主机应用程序而没有首先复制(更新的)Plugin.dll,并且它在组合期间抛出异常:

Unable to load one or more of the requested types. Retrieve the LoaderExceptions for more information

这当然是因为它试图导入的Plugin.dll实现了IPlugin的不同版本,其中属性/方法签名不匹配。虽然在受控和受监控的环境中很容易避免这种情况,但是通过简单地避免(duh)插件文件夹中过时的IPlugin实现,我不能在可能遇到传统插件的生产环境中依赖这样的假设。

问题在于此异常有效地破坏了整个Compose操作并导入了 no 导出。我宁愿忽略不匹配的IPlugin实现,以便仍然导入实现正确版本的IPlugin的目录中的其他导出。

有没有办法实现这个目标?我正在考虑以下几种可能的选择:

  • 在调用Compose之前或之后,在CompositionContainer上设置了一个标志(“忽略失败的导入”)
  • <ImportMany()>属性
  • 上指定了类似的标志
  • 有一种方法可以“挂钩”基于Compose()的迭代过程,并且能够单独处理每个(失败的)导入
  • 使用强名称签名以某种方式只查找实现当前版本的IPlugin的导入

想法?

3 个答案:

答案 0 :(得分:15)

我也遇到了a similar problem

如果您确定要忽略此类“坏”程序集,则解决方案是在创建每个程序集目录后立即调用AssemblyCatalog.Parts.ToArray()。这将触发您提及的ReflectionTypeLoadException。然后你有机会捕获异常并忽略坏组件。

为所有“好”程序集创建AssemblyCatalog个对象后,可以在AggregateCatalog中聚合它们并将其传递给CompositionContainer构造函数。

答案 1 :(得分:8)

这个问题可能是由几个因素引起的(加载的程序集上的任何异常),比如异常说,看看ExceptionLoader(希望)得到一些想法

我发现的另一个问题/解决方案是,当使用 DirectoryCatalog 时,如果您没有指定第二个参数“searchPattern”, MEF将加载该文件夹中的所有dll (包括第三方),并开始寻找也可能导致此问题的导出类型,解决方案是在导出类型的所有程序集上都有一个约定名称,并指定在DirectoryCatalog构造函数中,我使用* _Plugin .dll,这样MEF只会加载包含导出类型的程序集

在我的情况下,MEF正在加载一个NHibernate dll并在LoaderException上抛出一些程序集版本错误(这个错误可能发生在目录中的任何dll),这种方法解决了这个问题

答案 2 :(得分:7)

以下是上述方法的一个例子:

var di = new DirectoryInfo(Server.MapPath("../../bin/"));

        if (!di.Exists) throw new Exception("Folder not exists: " + di.FullName);

        var dlls = di.GetFileSystemInfos("*.dll");
        AggregateCatalog agc = new AggregateCatalog(); 

        foreach (var fi in dlls)
        {
            try
            {
                var ac = new AssemblyCatalog(Assembly.LoadFile(fi.FullName));
                var parts = ac.Parts.ToArray(); // throws ReflectionTypeLoadException 
                agc.Catalogs.Add(ac);
            }
            catch (ReflectionTypeLoadException ex)
            {
                Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
            }
        }

        CompositionContainer cc = new CompositionContainer(agc);

        _providers = cc.GetExports<IDataExchangeProvider>();