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

 

相关文章
相关标签/搜索