通过 MEF 容器处理组件?
我使用 MEF 将接口映射到实现类作为 DI 的一种方式。例如,我对接口使用 Import 属性,对实现类使用 Export 属性。我的理解是MEF框架将创建实现类实例并将它们保存在MEF的容器中以供使用或自动注入。
我的一些实现类实现了 IDispose 接口。由于实例是由 MEF 创建的,我认为如果 MEF 退出时组件是一次性的,我应该让 MEF 调用组件的 Dispose 方法。例如,在我的应用程序中,我保存了对 MEF 容器的引用。当应用程序终止时,我调用容器的 Dispose 方法。问题是我的组件的 Dispose 从未被调用。
以下是一些有关导入和导出映射的示例代码:
[Import]
private IMyInterface IComponent1 { get; set; }
....
[Export]
private IMyInterface Component {
get {
var instance = new MyImplemetation();
....
return instance;
}
}
....
对于其他映射,还有许多其他导入和导出定义,方法类似。我以这种方式构建映射,以便 MEF 了解关系以及如何创建映射实例的方式。以下是我的应用程序中使用 AssemblyCatalog 加载映射的一些代码:
var catalog = new AggregateCatalog();
catalog.Add (new AssemblyCatalog(Assembly.GetExecutingAssembly());
var batch = new CompositionBatch();
batch.AddPart(catalog);
// MEF container has all the mappings
var container = new CompositionContainer(catalog);
....
// Get instance from container
var instance = container.GetExportedValue<IMyInterface>();
// my instance CTOR has a contructor with several other
// implementation instances injected by interface
// instance starts to do its job and coordinates others ...
instance.Start();
....
// Finally the job is done.
// Dispose the container explicitly there.
container.Dispose();
// But my components are never disposed
// this results some connections not being closed
// file streams not being closed...
这里实例具有 MEF 通过 CTOR 注入的许多其他组件。这些组件还包含由 MEF 注入的其他组件。问题是,由于某些实例是共享的,因此很难决定何时处置组件。如果我对其中一个调用 Dispose,这将导致其他人无法使用它。正如您在此图中所看到的,实例由 MEF 创建并注入到我的应用程序类中。每个组件不应该了解其他组件,并且应该使用注入的组件来完成工作。
我不确定当应用程序终止或容器被处置时,我应该在哪里/如何指示 MEF 在组件上调用 Dispose?我应该调用组件的 Dispose 吗?我认为这是不对的,因为 MEF 创建它们并根据需要将它们注入客户端。客户在完成工作时不应调用 Dispose。
I use MEF to map interface to implementation class as a way of DI. For example, I use the Import attribute for an interface, and Export for implementation class. My understanding is that the MEF framework will create the implementation class instances and hold them in MEF's container for use or auto-injection.
Some of my implementation classes implement IDispose interface. Since instances are created by MEF, I think I should let the MEF to call components' Dispose method if they are disposable when the MEF is out. For example, in my application, I hold a reference to the MEF's container. When the application terminates, I call the Dispose method of the container. The problem is that my components' Dispose is never called.
Here are some example codes about the import and export mapping:
[Import]
private IMyInterface IComponent1 { get; set; }
....
[Export]
private IMyInterface Component {
get {
var instance = new MyImplemetation();
....
return instance;
}
}
....
There many other import and export definitions for other mappings in the similar way. I construct mappings in this way so that the MEF has the knowledge of the relationships and the way how to create the mapped instances. Here are some codes in my application to load mappings by using AssemblyCatalog:
var catalog = new AggregateCatalog();
catalog.Add (new AssemblyCatalog(Assembly.GetExecutingAssembly());
var batch = new CompositionBatch();
batch.AddPart(catalog);
// MEF container has all the mappings
var container = new CompositionContainer(catalog);
....
// Get instance from container
var instance = container.GetExportedValue<IMyInterface>();
// my instance CTOR has a contructor with several other
// implementation instances injected by interface
// instance starts to do its job and coordinates others ...
instance.Start();
....
// Finally the job is done.
// Dispose the container explicitly there.
container.Dispose();
// But my components are never disposed
// this results some connections not being closed
// file streams not being closed...
Here the instance has many other components injected through CTOR by the MEF. Those components also contain other components which are injected by the MEF. The problem is that it is really hard to make decision when to dispose components since some instances are shared. If I called Dispose on one, this would cause others not being able to use it. As you can see in this picture, instances are created by the MEF and injected to my application classes. Each component should not have any knowledge of others, and it should use injected components to do the job.
I am not sure where/how I should instruct the MEF to call Dispose on components when the application terminates or the container is disposed? Should I call the Dispose on components? I don't think that is right since the MEF creates them and inject them into clients as required. The clients should not call their Dispose when finishing their jobs.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
MEF 确实管理它创建的组件的生命周期。您的示例中的问题似乎是您想要处置的对象实际上并不是由 MEF 创建的。也许您想做这样的事情:
ComponentExporter 是 MEF 实际创建的类,如果它实现了 IDisposable,那么 MEF 将随容器一起处置它。在此示例中,ComponentExporter 在释放时释放创建的组件,这可能正是您想要的。
当然,如果直接将导出放在 MyImplementation 类上会更容易。我假设您有一些原因不这样做,但这就是它的外观:
关于代码的一些其他注释:您可能不需要通过批处理将目录添加到容器中,除非您将其导入到某个地方并从容器内的部件对其进行修改。如果您碰巧要处理许多请求并且关心性能,则应该只创建一次 AssemblyCatalog,然后对所有请求使用同一个。
MEF does manage the lifetime of the components it creates. It looks like the problem in your example is that the object you want disposed is not actually created by MEF. Perhaps you want to do something like this:
ComponentExporter is the class actually created by MEF, and if it implements IDisposable then MEF will dispose it with the container. In this example ComponentExporter disposes the created component when it is dispose, which is likely what you want.
Of course it would be easier if you just put the export on the MyImplementation class directly. I assume you have some reason for not doing that, but this is how it would look:
A few other notes on your code: You probably don't need to add the catalog to the container via the batch, unless you are importing it somewhere and modifying it from parts inside the container. And if you happen to be processing many requests and are concerned about performance, you should only create the AssemblyCatalog once, and then use the same one for all requests.
丹尼尔是对的。我将导入和导出的关系定义为映射类中的属性。我将它们作为 ComposablePartCatalog 加载到 MEF 的容器中,以便 MEF 可以神奇地动态获取相应的实例。在映射类中,我有一些到新实例的代码。因此,我必须想办法让MEF在退出进程时回调那些映射类来处理创建的资源。
我喜欢丹尼尔的建议,为我的导出部分引入一个类。由于我所有的 DI 映射都是以属性(getter 和 setter)的方式定义的,所以我创建了一个如下的基类:
有了这个基类,我的映射类将能够让 MEF 来完成处理工作。例如,这里有一个例子:
我所有的映射类都是以类似的方式定义的,而且它们更干净。基本原则是在类中创建的实例或资源应该在类中处理,但不能在注入的实例中处理。这样,我就不再需要通过 MEF 清理任何注入的实例,如本例所示:
顺便说一句,我喜欢使用属性作为我的导入和导出,因为属性与类的业务无关逻辑。在其他许多情况下,某些类来自第三方,我无法访问它们的源代码以将它们标记为导出。
Daniel is right. I defined a relationships of Import and Export as properties in my mapping classes. I loaded them as ComposablePartCatalog to MEF's container so that MEF can magically fetches corresponding instances on fly. It is within the mapping classes that I have some codes to new instances. Therefore, I have to find a way to let MEF to call back to those mapping classes to dispose the created resources when MEF is out of a process.
I like Daniel's suggestion to introduce a class for my Export part. Since all my DI mappings are defined in the way of properties (getter and setters), I created a base class like this:
With this base class, my mapping classes will be able to let MEF to do the disposing job. For example, here is one example:
All my mapping classes are defined in the similar way, and they are much cleaner. The basic principle is that instances or resources which are created within a class should be disposed within the class, but not injected instances. In this way, I don't need to clean up any injected instances by MEF any more, as in this example:
By the way, I like to use properties as my imports and exports since the attributes have nothing to do a class' business logic. In other many cases, some classes are from third parties and I don't have access to their source codes to mark them as export.