MEF ImportMany导入为空但容器正常,求排查遗漏点
我之前也碰到过几乎一模一样的MEF导入问题!折腾了好久才找到核心原因,结合你的代码和描述,给你梳理几个最可能的遗漏点:
1. 最关键:接口必须是共享的同一类型
你提到“宿主程序集和导出DLL中各有自己的IOperation接口”——这就是问题的核心!MEF是基于**类型的完全限定名(包括程序集信息)**来匹配导入和导出的,两个不同程序集里的同名接口,在MEF看来是完全不相关的类型,自然无法匹配。
解决方法:
把IOperation和IOperationData接口提取到一个独立的类库项目(比如Common.Contracts.dll),然后让宿主程序和导出DLL都引用这个类库,确保两边使用的是同一个接口类型。微软官方教程里的案例其实默认是共享接口的,这一点很容易被忽略。
2. 导出类的访问修饰符必须是public
看你的导出类代码:
[Export(typeof(IOperation))] [ExportMetadata("Type", "myType")] class Operation: IOperation { // 类逻辑 }
这里的Operation是默认的internal访问级别,而MEF默认只能发现并导出public类型!这是非常容易踩的坑,把类改成public就能解决一大半问题:
public class Operation: IOperation { ... }
3. 简化组合逻辑,避免冗余调用
你的构造函数里同时调用了Compose(batch)、ComposeParts(this)、SatisfyImportsOnce(this),这不仅冗余,还可能导致冲突。正确的组合逻辑应该是这样的:
public HostClass() { var catalog = new AggregateCatalog(); catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly())); catalog.Catalogs.Add(new DirectoryCatalog(@"Path/to/Folder")); _container = new CompositionContainer(catalog); try { // 只需要这一行,就能完成当前实例的导入组合 this._container.ComposeParts(this); } catch (CompositionException compositionException) { Console.WriteLine(compositionException.ToString()); } }
ComposeParts(this)已经负责将当前实例的导入与目录中的导出匹配SatisfyImportsOnce适用于不需要跟踪变更的一次性导入,这里完全没必要- 你添加的
batch.AddExportedValue(this._container)如果没有其他地方导入这个容器,属于多余操作,建议移除
4. 验证元数据与导入泛型的匹配
你的导入是IEnumerable<Lazy<IOperation, IOperationData>>,需要确保IOperationData接口的属性名和ExportMetadata的键完全匹配(大小写敏感)。比如:
// 元数据接口 public interface IOperationData { string Type { get; } }
这样[ExportMetadata("Type", "myType")]才能正确映射到IOperationData.Type属性。
5. 确认DirectoryCatalog的路径正确性
确保@"Path/to/Folder"是正确的路径:
- 优先使用绝对路径测试,比如
@"C:\MyApp\Plugins" - 或者用相对路径结合程序集目录:
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins") - 可以通过调试查看
catalog.Parts.Count,确认目标DLL的导出是否被加载到目录中
按以上步骤排查,应该就能解决你的问题了!
内容的提问来源于stack exchange,提问作者Dominik

