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

 
共享,非共享与所有权(Share,Non Shared and ownership)
 
部件的共享性(Shareability)是通过使用 PartCreationPolicyAttribute 定义的。PartCreationPolicyAttribute 提供以下几种值:
 
  • Shared:部件所有者告知 MEF 一个或多个部件的实例存在于容器。
  • NonShared: 部件所有者告知 MEF 每次对于部件导出的请求将会被一个新的实例处理。
  • Any 或者不支持的值: 部件所有者允许部件用作“Share”或者“NonShared”。
 
可以使用 [System.ComponentModel.Composition.PartCreationPolicyAttribute] 定义创建策略:
 
  1. [PartCreationPolicy(CreationPolicy.NonShared)]
  2. [Export(typeof(IMessageSender))]
  3. public class SmtpSender : IMessageSender
  4. {
  5. public void Send(string message)
  6. {
  7. throw new NotImplementedException();
  8. }
  9. }
  10.  
  11. public interface IMessageSender
  12. {
  13. void Send(string message);
  14. }
容器总会有他所创建部件的所有权。换言之,所有权绝不会转移给使用容器实例(直接)或者通过导入(间接)请求者。
导入也可以定义或者约束部件的创建策略,用于提供导入值。你说要做的是为 RequiredCreationPolicy 指定 CreationPolicy 枚举值:

 
  1. [Export]
  2. public class Importer
  3. {
  4. [Import(RequiredCreationPolicy = CreationPolicy.NonShared)]
  5. public Dependency Dep { get; set; }
  6. }
部件可用性关联到导入者是非常有用的。默认情况下,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 方法: 
 
  1. var batchProcessorExport = container.GetExport<IBatchProcessor>();
  2.  
  3. var batchProcessor = batchProcessorExport.Value;
  4. batchProcessor.Process();
  5.  
  6. container.ReleaseExport(batchProcessorExport);
下图描述一个对象图并显示哪些部件会被释放(引用移除,回收)哪些保持原状。

  
 

作为 root 部件是 non shared,容器不会保留引用,所以大体上是无操作的。我们继续遍历图检查为 root 部件的导出。部件1既 non shared 又 disposable,所以部件被回收而且引用从容器移除。同样发生在部件2,然后。。。。。。。。

 
注意那些深度优先方式实现的遍历图。
 
容器层级(Container hierarchies)

 
另一种方法处理同样的问题是使用层级容器。你可以创建容器并把他们连接到父容器,使之成为子容器。请注意,除非你为子容器提供不同的目录,这不会起到太大的作用,实例化同样会在父容器发生。
 
因此,或者你应该指定一个全新的目录,公开一组应该由子容器创建的部件。我们期望子容器的生命期是短暂的,创建的部件会提前释放和回收。
 
一种常见的方法是在父容器构建 Shared 部件以及在子容器上构建 Non Shared 部件。Shared 部件会依靠 Non Shared 部件导出,此外,主目录必须包括整组部件,反之,子容器应该仅仅包含主目录 non  shared 部件过滤的视图。
 
获取该主题的更多信心,请参考:过滤目录

 
回收序列

回收序列总是不确定的。这意味你不应该尝试在你的 Dispose 方法上使用导入。例如:
 
  1. [Export]
  2. public class SomeService : IDisposable
  3. {
  4. [Import]
  5. public ILogger Logger { get; set; }
  6.  
  7. public void Dispose()
  8. {
  9. Logger.Info("Disposing"); // might throw exception!
  10. }
  11. }
在 dispose 方法实现中使用导入的 logger 实例可能会出错,作为 ILogger 约定的实现也可能会被回收掉,或者已经被回收了。
 
AddPart/RemovePart
 
不是每个部件都是由容器创建。也可以从容器添加和移除部件。这个过程触发组合并且可能开始为满足依赖递归添加部件的创建。  

注意:MEF 永远不需要你提供实例的所有权,但是它确实有所创建的部件的所有权用以满足实例的导入。
 
  1. using System;
  2. using System.ComponentModel.Composition;
  3. using System.ComponentModel.Composition.Hosting;
  4. using System.ComponentModel.Composition.Primitives;
  5.  
  6. class Program
  7. {
  8. static void Main(string[] args)
  9. {
  10. var catalog = new AssemblyCatalog(typeof(Program).Assembly);
  11. var container = new CompositionContainer(catalog);
  12. var root = new Root();
  13.  
  14. // add external part
  15. container.ComposeParts(root);
  16.  
  17. // ... use the composed root instance
  18.  
  19. // removes external part
  20. batch = new CompositionBatch();
  21. batch.RemovePart(root);
  22. container.Compose(batch);
  23. }
  24. }
  25.  
  26. public class Root
  27. {
  28. [Import(RequiredCreationPolicy = CreationPolicy.NonShared)]
  29. public NonSharedDependency Dep { get; set; }
  30. }
  31.  
  32. [Export, PartCreationPolicy(CreationPolicy.NonShared)]
  33. public class NonSharedDependency : IDisposable
  34. {
  35. public NonSharedDependency()
  36. {
  37. }
  38.  
  39. public void Dispose()
  40. {
  41. Console.WriteLine("Disposed");
  42. }
  43. }

MEF 编程指南(九):部件生命周期的更多相关文章

  1. MEF初体验之九:部件生命周期

    理解MEF容器中部件的生命周期及其含义是非常重要的.鉴于MEF重点在开放端应用程序,这将变得尤其重要的,一旦app ships和第三方扩展开始运行,作为应用程序的开发者将很好地控制这一系列的部件.生命 ...

  2. MEF 编程指南(二):定义可组合部件和契约

    可组合部件(Composable Parts)   在 MEF 内部可组合部件是一个可组合单元.可组合部件导出其他可组合部件需要的服务,并且从其他可组合部件导入服务.在 MEF 编程模型中,可组合部件 ...

  3. Java并发编程:线程的生命周期是个怎样的过程?

    前言 在日常开发过程中,如果我们需要执行一些比较耗时的程序的话,一般来说都是开启一个新线程,把耗时的代码放在线程里,然后开启线程执行.但线程是会耗费系统资源的,如果有多个线程同时运行,互相之间抢占系统 ...

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

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

  5. Spring5参考指南:Bean的生命周期管理

    文章目录 Spring Bean 的生命周期回调 总结生命周期机制 startup和Shutdown回调 优雅的关闭Spring IoC容器 Spring Bean 的生命周期回调 Spring中的B ...

  6. MEF 编程指南(十二):批量组合

    MEF 容器实例并非不可变的.如果目录支持改变(像监控目录变动)或者在运行时添加/移除部件都可能发生改变.以前,你不得不做出改动并且调用 CompositionContainer 上的 Compose ...

  7. MEF 编程指南(十一):查询 CompositionContainer

    CompositionContainer 公开了一部分获取导出.导出对象以及两者集合的重载.   在这些方法重载中,你应该遵循下面的共享行为准则 - 除非特别说明.   当请求单一实例的时候,如果没发 ...

  8. MEF 编程指南(七):使用目录

    目录(Catalogs)   MEF 特性编程模型的核心价值,拥有通过目录动态地发现部件的能力.目录允许应用程序轻松地使用那些通过 Export Attribute 注册自身的导出.下面列出 MEF ...

  9. MEF 编程指南(十):重组

    有些应用程序被设计成在运行时动态地改变.例如,一个新的扩展可能被下载,或者其他原因变得不可用.MEF 依靠我们称之为重组(Composition)的技术处理,在初始化组合以后改变导入值的场景.   导 ...

随机推荐

  1. bq24075 锂电池 充电电路分析

     bq24075 锂电池 充电电路分析 本文主要是分析bq24075锂电池充电芯片电路,知道其大致是怎么工作的,其中的一些电阻该如何配置. -- 深圳 南山平山村 曾剑锋 一.参考文章: . NTC热 ...

  2. git - 搭建git仓库

    1. 更新git版本: http://codelife.me/blog/2013/06/25/upgrade-git-on-centos-6-4/ 2. 建立git仓库: git init --bar ...

  3. Java [Leetcode 223]Rectangle Area

    题目描述: Find the total area covered by two rectilinear rectangles in a 2D plane. Each rectangle is def ...

  4. Java 单元测试(Junit)

    在有些时候,我们需要对我们自己编写的代码进行单元测试(好处是,减少后期维护的精力和费用),这是一些最基本的模块测试.当然,在进行单元测试的同时也必然得清楚我们测试的代码的内部逻辑实现,这样在测试的时候 ...

  5. mysql 自旋锁

    自旋(spin)是一种通过不间断地测试来查看一个资源是否变为可用状态的等待操作,用于仅需要等待很短的时间等待所需资源的场景.使用自旋这种“空闲循环(busy-loop)”来完成资源等待的方式要比通过上 ...

  6. ASP.NET MVC+Bootstrap个人博客之后台dataTable数据列表(五)

    jQuery  dataTables 插件是一个优秀的表格插件,是后台工程师的福音!它提供了针对数据表格的排序.浏览器分页.服务器分页.查询.格式化等功能.dataTables 官网也提供了大量的演示 ...

  7. 无人机DLG生产作业流程

    参考文章 无人机(AVIAN)低空摄影测量作业流程 无人机低空遥感测绘作业流程及主要质量控制点 微型无人机低空摄影测量系 无人机航空摄影测量系统引进与发展 基于复杂地形的无人机航摄系统1∶500 DL ...

  8. UI篇--Android中TableLayout中的布局

        表格布局是按照行列来组织子视图的布局.表格布局包含一系列的Tablerow对象,用于定义行(也可以使用其它子对象).表格布局不为它的行.列和单元格显示表格线.每个行可以包含个以上(包括)的单元 ...

  9. DevExpress 中根据数据库字典动态生成卡式菜单 z

    第三方的Devexpress套件因为要使用权限机制控制不同用户进入系统显示菜单所以要配合字典数据动态生成.在WEB中这种问题灰常的轻松在winform里就稍微有点不同为了用DEV实现卡式菜单有组的概念 ...

  10. 1、ListView自定义控件下拉刷新(一)

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layo ...