Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dispose components by MEF container?

Tags:

c#

mef

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.

like image 575
David.Chu.ca Avatar asked Feb 24 '10 18:02

David.Chu.ca


1 Answers

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:

public class ComponentExporter : IDisposable
{
    private IMyInterface _component;

    [Export]
    public IMyInterface Component
    {
        get
        {
            if (_component != null)
            {
                _component = new MyImplementation();

                // ...
            }
            return _component;
        }
    }

    public void Dispose()
    {
        if (_component != null)
        {
            _component.Dispose();
        }
    }
}

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:

[Export(typeof(IMyInterface))]
public class MyImplementation : IMyInterface, IDisposable
{
    // ...
}

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.

like image 84
Daniel Plaisted Avatar answered Sep 20 '22 15:09

Daniel Plaisted