MEF初体验之九:部件生命周期
理解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部件都可以提供值。
|
|||||||||||||||||||
注意:当两边都定义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初体验之九:部件生命周期的更多相关文章
- MEF 编程指南(九):部件生命周期
理解 MEF 容器部件生命周期和实现是非常重要的事情.考虑到 MEF 关注可扩展应用程序.这变得尤为重要.生命期可以解释为期望部件的共享性(transitively, its exports) 共 ...
- MEF初体验之二:定义组合部件和契约
组合部件 在MEF中,一个组合部件就是一个组合单元,组合部件"出口"其它组合部件需要的服务并且从其它部件"进口"需要的服务.在MEF编程模型中,为了声明组合部件 ...
- MEF初体验之十一:查询组合容器
查询组合容器 组合容器暴露了几个get exports的重载方法和导出对象和对象集合.你需要注意下面的行为: 当请求单个对象实例时,如果未发现导出,一个异常将被抛出 当请求单个对象实例时,如果发现超过 ...
- MEF初体验之三:Exports声明
组合部件通过[ExportAttribute]声明exports.在MEF中,有这么几种成员可声明exports的方式:组合部件(类).字段.属性和方法.我们来看下ExportAttribute类的声 ...
- Angular4学习笔记(九)- 生命周期钩子简介
简介 Angular 指令的生命周期,它是用来记录指令从创建.应用及销毁的过程.Angular 提供了一系列与指令生命周期相关的钩子,便于我们监控指令生命周期的变化,并执行相关的操作.Angular ...
- MEF初体验之十:部件重组
一些应用程序被设计成在运行时可以动态改变.例如,一个新的扩展被下载,或者因为其它的多种多样的原因其它的扩展变得不可用.MEF处理这些多样的场景是依赖我们称作重组的功能来实现的,它可已在最初的组合后改变 ...
- MEF初体验之十二:Composition Batch
一个MEF容器实例是不可变的.如果catalog支持改变(像观察一个目录的改变)或是如果你的代码在运行时添加或移除部件,改变都可能发生.以前,你不得不作出改变并在组合容器上调用它的组合方法.在Prev ...
- MEF初体验之八:过滤目录
当在使用子容器的时候,基于某些具体标准来过滤目录可能是重要的.例如,基于部件的创建策略来过滤是很常见的.下面的代码片段演示了如何构建这种特别方法: var catalog = new Assembly ...
- MEF初体验之七:Using Catalogs
MEF特性化编程模型的价值主张之一是通过catalogs动态发现部件的能力.Catalogs允许应用程序很容易地消费那些通过[Export]已经自我注册的exports. Assembly Catal ...
随机推荐
- c#和UDP SOCKET广播
server: Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,ProtocolType.Udp); // ...
- 【python】按行切分文本文件
python脚本利用shell命令来实现文本的操作, 这些命令大大减少了我们的代码量. 比如按行切分文件并返回切分后得到的文件列表,可以利用内建的split命令进行切分.为了返回得到的文件列表名,可以 ...
- LeetCode——Valid Sudoku
Determine if a Sudoku is valid, according to: Sudoku Puzzles - The Rules. The Sudoku board could be ...
- Android该HTTP下载
今天学习了Android开发中比較难的一个环节,就是断点续传下载,非常多人看到这个标题就感觉头大.的确,假设没有良好的逻辑思维,这块的确非常难搞明确.以下我就将自己学到的知识和一些见解写下供那些在这个 ...
- Hive综合HBase——经Hive阅读/书写 HBase桌子
社论: 本文将Hive与HBase整合在一起,使Hive能够读取HBase中的数据,让Hadoop生态系统中最为经常使用的两大框架互相结合.相得益彰. watermark/2/text/aHR0cDo ...
- 使用Socket沟通
当两台电脑TCP/IP协议进行通讯.平时Socket对象来表示该通信接口的两端,并通过Socket生产I/O流进行网络通信. 其中ServerSocket对象可以接收从连接的其他通信实体的请求.这个目 ...
- Js常用技巧
摘录:http://crasywind.blog.163.com/blog/static/7820316920091011643149/ js 常用技巧 1. on contextmenu=" ...
- poj 1959 Darts 同意反复组合
水题.直接贴代码. //poj 1959 //sep9 #include <iostream> using namespace std; int n; int f[128]; int so ...
- (hdu step 7.1.2)You can Solve a Geometry Problem too(乞讨n条线段,相交两者之间的段数)
称号: You can Solve a Geometry Problem too Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/ ...
- hdu 5094 Maze(水搜索)
题目意思:说有一个人在(1,1) 他的目标点在(n,m) 每次是4方向的移动: 限制条件:有的各自之间有墙 或者门,强不可通过,有对应的要钥匙可以开启这个类型的所有门: 问题:求最少步骤数(和): 类 ...