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

MEF:“是的;无法加载一个或多个请求的类型。检索LoaderExceptions以获取更多信息”;,exception,plugins,mef,loader,Exception,Plugins,Mef,Loader,场景:我使用托管可扩展性框架在运行时根据单独dll中定义的接口契约加载插件(导出)。在我的VisualStudio解决方案中,我有3个不同的项目:宿主应用程序、一个类库(定义接口--“IPlugin”)和另一个实现接口的类库(导出--“MyPlugin.dll”) 主机在其自己的根目录中查找导出,因此在测试期间,我构建了整个解决方案,并将Plugin.dll从插件类库bin/release文件夹复制到主机的调试目录,以便主机的DirectoryCatalog能够找到它并将其添加到Composit

场景:我使用托管可扩展性框架在运行时根据单独dll中定义的接口契约加载插件(导出)。在我的VisualStudio解决方案中,我有3个不同的项目:宿主应用程序、一个类库(定义接口--“IPlugin”)和另一个实现接口的类库(导出--“MyPlugin.dll”)

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

但是,有几次我在没有先复制(更新的)Plugin.dll的情况下运行主机应用程序,并且它在合成过程中引发了一个异常:

无法加载一个或多个请求的类型。检索LoaderExceptions以了解更多信息

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

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

有没有办法做到这一点?我在考虑以下几种可能的选择之一:

  • 在调用Compose之前或调用Compose时,CompositionContainer上有一个标志(“忽略失败的导入”)
  • 属性上有一个类似的标志要指定
  • 有一种方法可以“钩住”到Compose()下面的迭代过程,并能够单独处理每个(失败的)导入
  • 使用强名称签名以某种方式仅查找实现当前版本IPlugin的导入
想法?

我也遇到过

如果确定要忽略此类“坏”程序集,则解决方案是在创建每个程序集目录后立即调用。这将触发您提到的
ReflectionTypeLoadException
。然后您就有机会捕获异常并忽略错误的程序集


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

此问题可能由多个因素引起(加载的程序集上的任何异常),如异常所述,查看例外加载程序(希望)了解一些信息

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


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

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>();
var di=new DirectoryInfo(Server.MapPath(“../../bin/”);
如果(!di.Exists)抛出新异常(“文件夹不存在:+di.FullName”);
var dlls=di.GetFileSystemInfos(“*.dll”);
AggregateCatalog agc=新的AggregateCatalog();
foreach(DLL中的var fi)
{
尝试
{
var ac=新的AssemblyCatalog(Assembly.LoadFile(fi.FullName));
var parts=ac.parts.ToArray();//引发ReflectionTypeLoadException
agc目录添加(ac);
}
捕获(ReflectionTypeLoadException ex)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
}
}
CompositionContainer cc=新的CompositionContainer(agc);
_providers=cc.GetExports();

我来看看。是的,我肯定我想忽略它们——反正我不能利用它们,它们会把我想要的东西都搞糟,所以这是一个不需要动脑筋的问题,不是吗?:)@d7samurai:有些人会通过谷歌搜索异常消息来结束这里。我只是想向他们强调,这并不能解决错误的根本原因。此外,引入静默失败(又称“错误恢复下一步”行为)也不是一件容易的事;它会使错误检测和诊断变得非常复杂。我建议至少给用户一些指示,说明发生了不好的事情。当然。所谓“忽略”,我的意思是“继续前进,而不让它停止整个进口过程”。没有用户,因为这是一个Windows服务,但此(引导程序)所做的一切都会被记录,包括正常操作和异常(包括在“LoaderExceptions”中转储所有异常,如果适用)。此外,在这种情况下,导入的目的是检测所有可用(引擎)插件,然后选择最合适的一个。而且总会有一个,因为基线引擎是嵌入在引导程序本身中的