2010-02-24 70 views
10

我使用MEF將接口映射到實現類作爲DI的一種方式。例如,我使用Import屬性作爲接口,Export作爲實現類。我的理解是,MEF框架將創建實現類實例,並將它們保存在MEF的容器中供使用或自動注入。通過MEF容器處理組件?

我的一些實現類實現了IDispose接口。因爲實例是由MEF創建的,所以我認爲我應該讓MEF調用組件的Dispose方法,如果它們在MEF不在時可以丟棄的話。例如,在我的應用程序中,我持有對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... 

這裏的實例已經通過CTOR由MEF注入許多其他部件。這些組件還包含由MEF注入的其他組件。問題在於,由於某些實例是共享的,因此很難決定何時處置組件。如果我打電話給Dispose,這會導致其他人無法使用它。正如你在這幅圖中看到的,實例是由MEF創建的,並注入到我的應用程序類中。每個組件不應該有任何其他知識,它應該使用注入的組件來完成這項工作。

我不確定在應用程序終止或處理容器時,我應該在哪裏/如何指示MEF調用Dispose?我應該在組件上調用Dispose嗎?我不認爲這是正確的,因爲MEF根據需要創建它們並將它們注入客戶。客戶在完成工作時不應該撥打Dispose。

回答

7

MEF確實管理它創建的組件的生命週期。它看起來像你的例子中的問題是,你想要處理的對象實際上不是由MEF創建的。也許你想要做這樣的事情:

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是實際上由MEF創建的類,並且如果它實現IDisposable然後MEF將與容器處置它。在這個例子中,ComponentExporter會在創建組件時處置它,這可能是您想要的。

當然,如果您直接將導出放在MyImplementation類上,它會更容易。我假定你有一些理由不這樣做,但這是它會怎樣看:

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

幾個在你的代碼的其他注意事項:你也許並不需要通過批量目錄添加到容器,除非您將它導入某處並從容器內的零件修改它。如果您碰巧正在處理許多請求並擔心性能問題,則應該只創建一次AssemblyCatalog,然後對所有請求使用相同的請求。

+0

我認爲Daniel很好地解釋了它。我在我的Export屬性獲取器中創建了實例。保持實例並從那裏清理是有意義的。我更喜歡將輸出放在getter而不是class上。我會測試它,並讓你知道這是否能解決問題。 – 2010-02-24 22:51:27

1

丹尼爾是對的。我在映射類中將導入和導出的關係定義爲屬性。我將它們作爲ComposablePartCatalog加載到MEF的容器中,以便MEF可以神奇地取回相應的實例。它在映射類中,我有一些代碼給新的實例。因此,當MEF不在進程中時,我必須找到一種方法讓MEF回調這些映射類來處理創建的資源。

我喜歡Daniel的建議,爲我的導出部分引入一個類。因爲我所有的DI映射在屬性(getter和setter方法)的方式定義,我創建了一個基類,像這樣:

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); 
    } 
} 

有了這個基類,我的映射類就能讓MEF做處理工作。例如,下面是一個例子:

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

我所有的映射類在類似的方式定義,他們是乾淨多了。基本原則是在類中創建的實例或資源應該放置在類中,而不是注入的實例。通過這種方式,我不需要通過MEF清理任何注入實例的任何更多,因爲在這個例子中:

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

順便說一句,我喜歡用性作爲我的進口和出口,因爲屬性有沒有什麼可以做一個類的業務邏輯。在其他許多情況下,有些類來自第三方,我無法訪問它們的源代碼將其標記爲導出。