2010-02-24 10 views
10

Ich verwende MEF, um die Schnittstelle zur Implementierungsklasse als eine Art von DI zuzuordnen. Zum Beispiel verwende ich das Import-Attribut für eine Schnittstelle und den Export für die Implementierungsklasse. Nach meinem Verständnis wird das MEF-Framework die Instanzen der Implementierungsklasse erstellen und sie im MEF-Container zur Verwendung oder automatischen Injektion speichern.Komponenten über MEF-Container entsorgen?

Einige meiner Implementierungsklassen implementieren die IDispose-Schnittstelle. Da Instanzen von MEF erstellt werden, sollte ich das MEF veranlassen, die Dispose-Methode der Komponenten aufzurufen, wenn sie verfügbar sind, wenn die MEF nicht verfügbar ist. In meiner Anwendung habe ich beispielsweise einen Verweis auf den Container des MEF. Wenn die Anwendung beendet wird, rufe ich die Dispose-Methode des Containers auf. Das Problem ist, dass die Dispose meiner Komponenten nie aufgerufen wird.

Hier sind einige Beispiel-Codes über den Import und Export-Mapping:

[Import] 
private IMyInterface IComponent1 { get; set; } 
.... 

[Export] 
private IMyInterface Component { 
    get { 
    var instance = new MyImplemetation(); 
    .... 
    return instance; 
} 
} 
.... 

Es gibt viele andere Import- und Exportdefinitionen für andere Zuordnungen in ähnlicher Weise. Auf diese Weise konstruiere ich Zuordnungen, so dass das MEF die Beziehungen und die Art und Weise, wie die zugeordneten Instanzen erstellt werden, kennt. Hier sind einige Codes in meiner Anwendung Zuordnungen zu laden, indem Sie Assembly mit:

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... 

hier die Instanz viele andere Komponenten durch CTOR durch den MEF injiziert hat. Diese Komponenten enthalten auch andere Komponenten, die von der MEF injiziert werden. Das Problem ist, dass es wirklich schwer ist, Entscheidungen zu treffen, wenn Komponenten entsorgt werden sollen, da einige Instanzen geteilt werden. Wenn ich bei einem Dispose anrufe, würde das dazu führen, dass andere es nicht benutzen können. Wie Sie in diesem Bild sehen können, werden Instanzen von der MEF erstellt und in meine Anwendungsklassen injiziert. Jede Komponente sollte keine Kenntnis von anderen haben, und sie sollte injizierte Komponenten verwenden, um die Aufgabe zu erfüllen.

Ich bin mir nicht sicher, wo/wie ich das MEF anweisen sollte, Dispose für Komponenten aufzurufen, wenn die Anwendung beendet oder der Container entsorgt wird? Soll ich die Dispose auf Komponenten aufrufen? Ich denke nicht, dass das richtig ist, da die MEF sie erstellt und sie bei Bedarf in die Kunden injiziert. Die Clients sollten ihre Dispose nicht aufrufen, wenn sie ihre Arbeit beendet haben.

Antwort

7

MEF verwaltet die Lebensdauer der von ihm erstellten Komponenten. Es sieht so aus, als ob das Problem in Ihrem Beispiel darin besteht, dass das zu entsorgende Objekt nicht von MEF erstellt wird. Vielleicht möchten Sie, so etwas tun:

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 ist die Klasse tatsächlich von MEF erstellt, und wenn es IDisposable implementiert, dann wird MEF es mit dem Behälter entsorgen. In diesem Beispiel stellt ComponentExporter die erstellte Komponente zur Verfügung, wenn sie verfügbar ist. Dies ist wahrscheinlich das, was Sie möchten.

Natürlich wäre es einfacher, wenn Sie den Export direkt auf die MyImplementation-Klasse setzen. Ich nehme an, Sie haben einen Grund für das nicht tun, aber das ist, wie es aussehen würde:

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

Ein paar andere Notizen auf Ihrem Code: Sie wahrscheinlich nicht brauchen, um den Katalog zu dem Behälter über den Batch hinzufügen , es sei denn, Sie importieren es irgendwo und ändern es von Teilen innerhalb des Containers. Und wenn Sie viele Anfragen bearbeiten und sich Gedanken über die Leistung machen, sollten Sie das AssemblyCatalog nur einmal erstellen und dann dasselbe für alle Anfragen verwenden.

+0

Ich denke, dass Daniel es schön erklärt hat. Ich habe die Instanz in meinem Export-Eigenschaft-Getter erstellt. Es macht Sinn, die Instanz zu halten und von dort zu reinigen. Ich bevorzuge es, den Export auf Getter statt auf Klasse zu setzen. Ich werde es testen und Sie wissen lassen, ob dadurch das Problem behoben wird. –

1

Daniel hat Recht. Ich habe eine Beziehung von Import und Export als Eigenschaften in meinen Mapping-Klassen definiert. Ich habe sie als ComposeablePartCatalog in den MEF-Container geladen, so dass MEF die entsprechenden Instanzen beim Fliegen magisch holen kann.Innerhalb der Mapping-Klassen habe ich einige Codes für neue Instanzen. Daher muss ich eine Möglichkeit finden, MEF dazu zu bringen, diese Mapping-Klassen zurückzurufen, um die erstellten Ressourcen zu entsorgen, wenn MEF außer Betrieb ist.

Ich mag Daniels Vorschlag, eine Klasse für meinen Exportteil einzuführen. Da alle meine DI Zuordnungen in der Art und Weise von Eigenschaften (Getter und Setter) definiert sind, habe ich eine Basisklasse wie folgt aus:

public class ComponentExporterBase: IDisposable { 
    private List<IDisposable> _list; 

    public ComponentExporterBase() { 
    _list = new List<IDisposable>(); 
    } 

    protect void Add(IDisposable obj) { 
    _list.Add(obj); 
    } 

    protected virtual void Dispose(bool disposing) { 
    if (disposing) { 
     foreach(var obj in _list) { 
     obj.Dispose(); 
     } 
     _list.Clear(); 
    } 
    } 

    public void Dispose() { 
    Dispose(true); 
    } 
} 

Mit dieser Basisklasse, meine Mapping-Klassen können MEF lassen das zu tun Entsorgungsaufgabe. Zum Beispiel hier ein Beispiel:

internal class MyDIMappingClass : ComponentExporterBase { 
    [Import] 
    private IDataReader _dataReader { get; set; } 

    [Export] 
    private IController { 
     get { 
     var reader = _dataReader; 
     var instance = new MyMainController(reader); 
     base.Add(instance); 
     return instance; 
    } 
    ... 
} 

Alle meine Mapping-Klassen sind in ähnlicher Weise definiert, und sie sind viel sauberer. Das Grundprinzip besteht darin, dass Instanzen oder Ressourcen, die innerhalb einer Klasse erstellt werden, innerhalb der Klasse angeordnet sein sollten, jedoch keine injizierten Instanzen. Auf diese Weise muss ich nicht mehr alle injiziert Instanzen von MEF zu reinigen, wie in diesem Beispiel:

public class MyMainController : IController { 
    private IDataReader _dataReader; 

    // dataReader is injected through CTOR 
    public MyMainControler(IDataReader dataReader) { 
    _dataReader = dataReader; 
    ... 
    } 
    ... 
    public void Dispose() { 
    // dispose only resources created in this class 
    // _dataReader is not disposed here or within the class! 
    ...} 
} 

By the way, ich mag Eigenschaften wie meine Importe und Exporte verwenden, da die Attribute haben nichts, um die Geschäftslogik einer Klasse zu machen. In anderen Fällen stammen einige Klassen von Dritten, und ich habe keinen Zugriff auf ihre Quellcodes, um sie als Export zu kennzeichnen.