理解MEF容器中部件的生命周期及其含义是非常重要的。鉴于MEF重点在开放端应用程序,这将变得尤其重要的,一旦app ships和第三方扩展开始运行,作为应用程序的开发者将很好地控制这一系列的部件。生命周期可以被解释为这样一个部件期望的共享物,无论是一个新的部件被创建还是一个部件被关闭或释放都由控制策略来翻译。

Shared, Non Shared and ownership

通过使用PartCreationPolicyAttribute特性设置CreationPolicy(类级别)来定义一个部件的共享物。下面的值是受支持的:
Shared:部件作者告诉MEF,一个部件的实例可以存在在每一个容器中(指定将由容器创建关联的该ComposablePart 的单个共享实例,并由所有请求者共享该实例)
NonShared:部件作者告诉MEF,一个部件每一次的导出请求都将由一个部件新的实例来提供服务。(指定将由容器为每个请求者创建一个关联的该ComposablePart的新的非共享实例)
Any or not supplied value:部件作者允许部件既可以支持Shared,也可以支持NonShared.

可以使用[System.ComponentModel.Composition.PartCreationPolicyAttribute]特性在一个部件上定义创建策略:

[PartCreationPolicy(CreationPolicy.NonShared)]
[Export(typeof(IMessageSender))]
public class SmtpSender : IMessageSender
{
}

这个容器将一直拥有它创建的部件的所有权。换句话说,该所有权从不会转移到一个通过使用容器实例(直接地)或一个导入(间接地)来请求它的行动者上来。

导入也可以定义或者约束这种被用来提供导入值的部件策略创建。你需要做的一切是为RequiredCreationPolicy指定CreationPolicy枚举值:

[Export]
public class Importer
{
[Import(RequiredCreationPolicy=CreationPolicy.NonShared)]
public Dependency Dep { get; set; }
}

对于与importer相关的部件需要使用共享的场景来说是很用的。默认地,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

注意:当两边都定义CreationPolicy为Any,结果它将是一个Shared部件。

来个例子:

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks; namespace PartLifetime
{
class Program
{
[ImportMany(RequiredCreationPolicy=CreationPolicy.Shared)]
public IEnumerable<IMessageSender> Senders { get; set; }
static void Main(string[] args)
{
Program p = new Program();
p.Compose();
foreach (var item in p.Senders)
{
item.Send("Hi,MEF");
}
Console.ReadKey();
}
void Compose()
{
AssemblyCatalog catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
}
interface IMessageSender
{
void Send(string msg);
}
[Export(typeof(IMessageSender))]
[PartCreationPolicy(CreationPolicy.Shared)]
class EmailSender : IMessageSender
{
public void Send(string msg)
{
Console.WriteLine("Email sent:" + msg);
}
}
[Export(typeof(IMessageSender))]
[PartCreationPolicy(CreationPolicy.NonShared)]
class SMSSender : IMessageSender
{
public void Send(string msg)
{
Console.WriteLine("SMS sent:" + msg);
}
} }

我们发现,当Import的请求创建策略为Shared,则自动匹配EmailSender组件;当为NonShared时,自动匹配SMSSende组件;当省略或者为Any时,自动匹配Shared和NonShared。

释放容器

通常,一个容器实例是部件生命周期的持有者。由容器创建的部件实例拥有的生命周期取决于容器的生命周期。标志容器生命周期结束的方式是释放它。释放一个容器的含义是:

  • 实现IDisposable接口的部件将会调用Dispose方法
  • 被容器占有的部件的引用将被清除
  • Shared组件将被释放和清除
  • Lazy导出组件在容器被释放后将不会工作
  • 操作可能会抛出System.ObjectDisposedExecption

容器和部件引用

我们相信,.Net GC是适当地清理可依赖的最好的东西。然后,我们也需要提供一个有确定性行为的容器。因此,这个容器将不会拥有它创建的部件的引用,除非下面的条件之一成立:

  • 这个部件被标记为Shared
  • 这个部件实现了IDisposable接口
  • 一个或者多个部件被配置为允许重组

对于那些条件,部件引用将被容器拥有。结合这个事实,你可以有NonShared部件,并且一直从容器来请求它们,然后内存需求将迅速成为一个问题。为了减轻这个问题,你应该依靠在下面接下来的两个话题的讨论的策略。

作用域操作和资源提前回收

一些常见类型的应用程序,像web apps和windows服务,在每个桌面应用上却又很大不同。它们可能更加依赖批量简短的操作。例如,一个windows服务可能会直接地监视,一旦一批可预估的文件存在,就将开始一个批处理操作来转换这些文件成另外一种格式。Web操作可能由每次请求操作所决定。

对于那些场景,你应该使用子容器或者提前释放对象。后者可以使容器释放和清掉非共享的部件。

为了提前释放对象,你需要调用由组合容器暴露的ReleaseExport方法。

var batchProcessorExport = container.GetExport<IBatchProcessor>();

var batchProcessor = batchProcessorExport.Value;
batchProcessor.Process(); container.ReleaseExport(batchProcessorExport);

容器分层

另一种解决相同问题的方式是使用容器分层。你可以创建容器并将它连接到一个父容器并作为其子容器。注意除非你提供了一个不同的catalog到子容器中,否则将不会有很大帮助,因为仍然会在父容器中实例化。

因此,你应该做的是,基于一种标准过滤父容器,这种标准是应该被创建在父容器中的一系列部件和那些应该被创建在子容器中的部件区分开来,或者是,你应该完全指定一个新的catalog来暴露一系列应该被创建在子容器中的部件。子容器正如所期望的那样是短期存在的,创建在它里面的部件将会更早地被释放掉。一个通用的解决办法是将共享的部件创建在父容器中,而将非共享的部件创建在子容器中。由于共享部件可能会依赖由非共享部件提供的导出,这时主catalog必须包含整个一系列的部件而子容器应该有一个仅包含非共享部件的过滤主容器的视图。

可处理命令

可处理命令并不能以任何方式确保。那意味着你不应该在你的dispose方法上试图使用导入。例如:

[Export]
public class SomeService : IDisposable
{
[Import]
public ILogger Logger { get; set; } public void Dispose()
{
Logger.Info("Disposing"); // might throw exception!
}
}

在你的dispose方法实现上使用导入的logger实例可能会有问题,因为这个ILogger契约的实现也可能是可处理的,而此时可能它已经被处理掉了。

添加部件/移除部件

并不是每一个部件都是由容器创建的。你也可以从容器中添加和移除部件。这个过程触发了容器,
使其开始创建部件来满足递归添加的部件的依赖。当部件被移除时,MEF足够聪明,它将会回收资源并且处理掉被部件添加的非共享部件。

注意:MEF将从不会占有你提供的实例的所有权,但是,它有由它自己创建的满足你实例导入的部件的所有权。

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace PartLifetime
{
class Example
{
static void Main()
{
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 //remove external part
//var batch = new CompositionBatch();
//var rootPart = batch.AddExportedValue<Root>(new Root());
//container.Compose(batch);
//batch = new CompositionBatch();
//batch.RemovePart(rootPart);
//container.Compose(batch);
container.ReleaseExport<NonSharedDependency>(new Lazy<NonSharedDependency>());
Console.ReadKey();
}
}
class Root
{
[Import(RequiredCreationPolicy=CreationPolicy.NonShared)]
public NonSharedDependency Dep { get; set; }
}
[Export,PartCreationPolicy(CreationPolicy.NonShared)]
class NonSharedDependency : IDisposable
{
public void Dispose()
{
Console.WriteLine("Disposed");
}
} }

MEF初体验之九:部件生命周期的更多相关文章

  1. MEF 编程指南(九):部件生命周期

    理解 MEF 容器部件生命周期和实现是非常重要的事情.考虑到 MEF 关注可扩展应用程序.这变得尤为重要.生命期可以解释为期望部件的共享性(transitively, its exports)   共 ...

  2. MEF初体验之二:定义组合部件和契约

    组合部件 在MEF中,一个组合部件就是一个组合单元,组合部件"出口"其它组合部件需要的服务并且从其它部件"进口"需要的服务.在MEF编程模型中,为了声明组合部件 ...

  3. MEF初体验之十一:查询组合容器

    查询组合容器 组合容器暴露了几个get exports的重载方法和导出对象和对象集合.你需要注意下面的行为: 当请求单个对象实例时,如果未发现导出,一个异常将被抛出 当请求单个对象实例时,如果发现超过 ...

  4. MEF初体验之三:Exports声明

    组合部件通过[ExportAttribute]声明exports.在MEF中,有这么几种成员可声明exports的方式:组合部件(类).字段.属性和方法.我们来看下ExportAttribute类的声 ...

  5. Angular4学习笔记(九)- 生命周期钩子简介

    简介 Angular 指令的生命周期,它是用来记录指令从创建.应用及销毁的过程.Angular 提供了一系列与指令生命周期相关的钩子,便于我们监控指令生命周期的变化,并执行相关的操作.Angular ...

  6. MEF初体验之十:部件重组

    一些应用程序被设计成在运行时可以动态改变.例如,一个新的扩展被下载,或者因为其它的多种多样的原因其它的扩展变得不可用.MEF处理这些多样的场景是依赖我们称作重组的功能来实现的,它可已在最初的组合后改变 ...

  7. MEF初体验之十二:Composition Batch

    一个MEF容器实例是不可变的.如果catalog支持改变(像观察一个目录的改变)或是如果你的代码在运行时添加或移除部件,改变都可能发生.以前,你不得不作出改变并在组合容器上调用它的组合方法.在Prev ...

  8. MEF初体验之八:过滤目录

    当在使用子容器的时候,基于某些具体标准来过滤目录可能是重要的.例如,基于部件的创建策略来过滤是很常见的.下面的代码片段演示了如何构建这种特别方法: var catalog = new Assembly ...

  9. MEF初体验之七:Using Catalogs

    MEF特性化编程模型的价值主张之一是通过catalogs动态发现部件的能力.Catalogs允许应用程序很容易地消费那些通过[Export]已经自我注册的exports. Assembly Catal ...

随机推荐

  1. VC6.0入门使用

    软件下载地址 http://pan.baidu.com/s/1qWuqFAO 新建win console 32 project,然后新建header文件.最后新建source cpp文件.如图所看到的

  2. Eclipse 快捷键整理

    Alt+/:代码提示Ctrl+/:注释/取消注释Ctrl+D:删除光标所在行Ctrl+K:将光标停留在变量上,按Ctrl+K键可以查找到下一个同样的变量Shift+Ctrl+K:和Ctrl+K查找的方 ...

  3. ZOJ 3795 Grouping 求最长链序列露点拓扑

    意甲冠军:特定n积分.m向边条. 该点被划分成多个集合随机的每个集合,使得2问题的关键是无法访问(集合只能容纳一个点) 问至少需要被分成几个集合. 假设没有戒指,接着这个话题正在寻求产业链最长的一个有 ...

  4. 索尼 LT26I刷机包 X.I.D 增加官方风格 GF A3.9.4 各方面完美

    ROM介 FX_GF_A系列是具有官方风格的.稳定的.流畅的.省电的.新功能体验的.最悦耳音效体验的ROM. FX_GF_A更新日志 ☆ GF_3.9.4 更新信息 ☆ 更新播放器 ☆ 更新adsp数 ...

  5. 浏览器被劫持到http://hao.169x.cn/?v=108的解决办法

    不管什么浏览器打开都是 http://hao.169x.cn/?v=108 ​1.下载wmi tool,(微软官网下载,我的下载地址是: http://120.52.73.52/download.mi ...

  6. python 调用图灵机器人api实现简单的人机交互

    接入流程例如以下,须要先注冊开发人员帐号,之后会得到一个32位的key,保存下来,用于以后发送数据.http://www.tuling123.com/ 请求方式 演示样例: # -*- coding: ...

  7. Windows Phone 8 - Runtime Location API - 1

    原文:Windows Phone 8 - Runtime Location API - 1 在Windows Phone里要做Background Service的方式,除了Background Ag ...

  8. 【白痴弟弟和你加强应用层】阅读 Develop API Guides 思考(一个)

    转载请注明出处:http://blog.csdn.net/zhaokaiqiang1992 动态检測设备属性 关于targetSdkVersion的含义 关于onSaveInstanceState的高 ...

  9. MyBatis学习门户网站(一)

    需要jar包:mybatis-3.x.x.jar .假设需要和spring综合,此外,我们需要增加相关的包 1:看到项目文件夹 不要在意红色 2:依照步骤: 1:增加jar包 2:创建数据源(conf ...

  10. Windows在结构Eclipse+Android4.0开发环境

    官方搭建步骤: http://developer.android.com/index.html 搭建好开发环境之前须要下载以下几个文件包: 一.安装Java执行环境JRE(没这个Eclipse执行不起 ...