场景:我正在使用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的目录中的其他导出。
有没有办法实现这个目标?我正在考虑以下几种可能的选择:
<ImportMany()>
属性想法?
答案 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>();