MEF 編程指南(九):部件生命週期

理解 MEF 容器部件生命週期和實現是很是重要的事情。考慮到 MEF 關注可擴展應用程序。這變得尤其重要。生命期能夠解釋爲指望部件的共享性(transitively, its exports)html

 
共享,非共享與全部權(Share,Non Shared and ownership)
 
部件的共享性(Shareability)是經過使用 PartCreationPolicyAttribute 定義的。PartCreationPolicyAttribute 提供如下幾種值:
 
  • Shared:部件全部者告知 MEF 一個或多個部件的實例存在於容器。
  • NonShared: 部件全部者告知 MEF 每次對於部件導出的請求將會被一個新的實例處理。
  • Any 或者不支持的值: 部件全部者容許部件用做「Share」或者「NonShared」。
 
可使用 [System.ComponentModel.Composition.PartCreationPolicyAttribute] 定義建立策略:
 
    [PartCreationPolicy(CreationPolicy.NonShared)]
    [Export(typeof(IMessageSender))]
    public class SmtpSender : IMessageSender
    {
        public void Send(string message)
        {
            throw new NotImplementedException();
        }
    }
 
    public interface IMessageSender
    {
        void Send(string message);
    }

 

容器總會有他所建立部件的全部權。換言之,全部權毫不會轉移給使用容器實例(直接)或者經過導入(間接)請求者。
導入也能夠定義或者約束部件的建立策略,用於提供導入值。你說要作的是爲 RequiredCreationPolicy 指定 CreationPolicy 枚舉值:
 
    [Export]
    public class Importer
    {
        [Import(RequiredCreationPolicy = CreationPolicy.NonShared)]
        public Dependency Dep { get; set; }
    }

 

部件可用性關聯到導入者是很是有用的。默認狀況下,RequiredCreationPolicy 被設置成 Any,因此 Shared 和 NonShared 部件均可以提供值。

- Part.Any Part.Shared Part.NonShared
Import.Any Shared Shared Non Shared
Import.Shared Shared Shared No Match
Import.NonShared Non Shared No Match Non Shared
 
注意:當雙方都定義爲「Any」的時候,結果會是 Shared 部件
 
釋放容器(Disposing the container)
 
容器實例一般是容器持有部件的生命週期。部件實例由容器建立,生命週期受到容器生命週期的限制。結束容器生命週期的途徑是調用 Disposing 方法。
  • 實現 IDisposable 的部件會調用 Dispose 方法
  • 容器中包含的部件引用將會被清除
  • 共享部件會被釋放和清除
  • 容器釋放後,延遲導出不會起做用
  • 該操做可能會拋出 System.ObjectDisposedException 異常
 
容器和部件引用(Container and parts references)
 
咱們相信 .NET 垃圾回收器是作清理最適合的選擇。然而,咱們也須要提供一個擁有肯定性行爲的容器。所以,除非知足下面的條件容器,將不會保留它所建立的引用:
 
  • 部件被標記爲 Shared
  • 部件實現了 IDisposable 接口
  • 一個或多個導入配置爲容許重組
 
對於上述狀況,部件引用是保留的。結合實際目標,從容器中請求那些非共享部件,內存需求會很快成爲一個問題。爲了緩解這個問題,應該依靠接下來兩節討論的策略。
 
域操做和早期資源回收(Scoped operations and early reclaim of resources)
 
一些常見應用程序,好比:Web 應用程序(web apps) 和 Windows 服務(windows services)與桌面應用程序(Desk Applications)有很大的區別。他們更多的依靠批處理和短暫操做。

對於那些場景,你應該要麼使用子容器(下一節介紹)要麼提早釋放對象圖。後者容許容器釋放和清理對於對象圖中 Shared 部件的引用 - 直到到達 Shared 部件。
 
爲了提早釋放對象圖,你須要調用 CompositionContrainer 公開的 ReleaseExport 方法: 
 
var batchProcessorExport = container.GetExport<IBatchProcessor>();
 
var batchProcessor = batchProcessorExport.Value;
batchProcessor.Process();
 
container.ReleaseExport(batchProcessorExport);

 

下圖描述一個對象圖並顯示哪些部件會被釋放(引用移除,回收)哪些保持原狀。
  
 


做爲 root 部件是 non shared,容器不會保留引用,因此大致上是無操做的。咱們繼續遍歷圖檢查爲 root 部件的導出。部件1既 non shared 又 disposable,因此部件被回收並且引用從容器移除。一樣發生在部件2,而後。。。。。。。。
 
注意那些深度優先方式實現的遍歷圖。
 
容器層級(Container hierarchies)
 
另外一種方法處理一樣的問題是使用層級容器。你能夠建立容器並把他們鏈接到父容器,使之成爲子容器。請注意,除非你爲子容器提供不一樣的目錄,這不會起到太大的做用,實例化一樣會在父容器發生。
 
所以,或者你應該指定一個全新的目錄,公開一組應該由子容器建立的部件。咱們指望子容器的生命期是短暫的,建立的部件會提早釋放和回收。
 
一種常見的方法是在父容器構建 Shared 部件以及在子容器上構建 Non Shared 部件。Shared 部件會依靠 Non Shared 部件導出,此外,主目錄必須包括整組部件,反之,子容器應該僅僅包含主目錄 non  shared 部件過濾的視圖。

 
獲取該主題的更多信心,請參考: 過濾目錄
 
回收序列

回收序列老是不肯定的。這意味你不該該嘗試在你的 Dispose 方法上使用導入。例如:
 
[Export]
public class SomeService : IDisposable
{
    [Import]
    public ILogger Logger { get; set; }
   
    public void Dispose()
    {
         Logger.Info("Disposing"); // might throw exception!
    }
}

 

在 dispose 方法實現中使用導入的 logger 實例可能會出錯,做爲 ILogger 約定的實現也可能會被回收掉,或者已經被回收了。
 
AddPart/RemovePart
 
不是每一個部件都是由容器建立。也能夠從容器添加和移除部件。這個過程觸發組合而且可能開始爲知足依賴遞歸添加部件的建立。  

注意:MEF 永遠不須要你提供實例的全部權,可是它確實有所建立的部件的全部權用以知足實例的導入。
 
using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
 
class Program
{
    static void Main(string[] args)
    {
        var catalog = new AssemblyCatalog(typeof(Program).Assembly);
        var container = new CompositionContainer(catalog);
        var root = new Root();
 
        // add external part
        container.ComposeParts(root);
 
        // ... use the composed root instance
 
        // removes external part
        batch = new CompositionBatch();
        batch.RemovePart(root);
        container.Compose(batch);
    }
}
 
public class Root
{
    [Import(RequiredCreationPolicy = CreationPolicy.NonShared)]
    public NonSharedDependency Dep { get; set; }
}
 
[Export, PartCreationPolicy(CreationPolicy.NonShared)]
public class NonSharedDependency : IDisposable
{
    public NonSharedDependency()
    {
    }
 
    public void Dispose()
    {
        Console.WriteLine("Disposed");
    }
}

 

相關文章
相關標籤/搜索