[.NET] 浅谈可扩展性框架:MEF

之前在使用Prism框架时接触到了可扩展性框架MEF(Managed Extensibility Framework),体验到MEF带来的极大的便利性与可扩展性。

此篇将编写一个可组合的应用程序,帮助大家快速熟悉MEF并将其应用于实际项目中。

有关MEF中的名词含义及功能实现,请大家移步:火车票

介绍下将要编写的Demo程序(下图),使用winform开发。

  • 通过组合操作,程序动态加载可用部件进行组合操作。
  • 通过解体操作,程序卸载所加载的所有部件。

新建项目后需引用程序集:

System.ComponentModel.Composition

主程序的核心代码如下:

public partial class Form1 : Form, IPartImportsSatisfiedNotification
  {
        [ImportMany(AllowRecomposition = true)]
        private IEnumerable<Lazy<IPlugin, IPluginMetadata>> plugins;

        private AggregateCatalog catalog;
        private CompositionContainer container;
        public Form1()
        {
            InitializeComponent();
            if (catalog == null)
                catalog = new AggregateCatalog();
            this.container = new CompositionContainer(catalog);
            this.container.ComposeParts(this);
        }

        #region Implementation of IPartImportsSatisfiedNotification
        public void OnImportsSatisfied()
        {
            flowLayoutPanel1.Controls.Clear();
            if (plugins != null && plugins.Count() != 0)
            {
                plugins.ToList().ForEach((a) =>
                {
                    Button btn = new Button();
                    btn.Cursor = System.Windows.Forms.Cursors.Hand;
                    btn.Width = 100;
                    btn.Height = 50;
                    btn.Text = a.Metadata.ThePluginName;
                    btn.Click += (d, b) => { a.Value.Run(); };
                    flowLayoutPanel1.Controls.Add(btn);
                });
            }
        }
        #endregion

        public void CompositionAction()
        {
            catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
        }
        ......
  }

1. IPartImportsSatisfiedNotification接口 : 在组件导入完成后,调用该接口中的方法(OnImportsSatisfied)。

2. MEF中最常用目录有三种:程序集目录(AssemblyCatalog),文件目录(DirectoryCatalog),聚合目录(AggregateCatalog)

程序集目录(AssemblyCatalog): 顾名思义可以向目录中添加程序集已存在类型中寻找可用于导入的部件。

	var catalog = new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly());

文件夹目录(DirectoryCatalog):从文件夹中寻找可用于导入的部件

var catalog = new DirectoryCatalog("Extensions");

聚合目录(AggregateCatalog):可以向聚合目录包含上述两种方式

var catalog =new AggregateCatalog(
  new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly()),
  new DirectoryCatalog("Extensions"));

3. CompositionContainer : 组合容器,管理部件的组合并提供了一系列用于创建部件实例扩展方法等,详细资料

4. 目录与容器的一般使用方法:

	var catalog =new AggregateCatalog();
    var container =new CompositionContainer(catalog);
    var container.ComposeParts(this);

5. 导入:ImportAttribute 与 ImportManyAttribute

用于以下三种用途:字段,属性,方法

	[import]
	private IData _data;

	[import]
	public IData Data{set;get;}

	[import]
	public Action ClickAction;

ImportManyAttribute : 通过组合容器将所有符合契约的导出进行填充 (真别扭,说白了就是导入多个)

	[ImportMany]
	private IEnumerable<iplugin> plugins;</iplugin>

AllowRecomposition : 是否允许重组

AllowRecomposition = true : 比如在程序运行的过程中,动态向聚合目录中添加可导出的部件,可以引发重组操作

	catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));

需要注意的是:DirectoryCatalog 不会引发重组操作,可通过Refresh方法指定重组的策略。

6. System.Lazy<T> : MEF提供延迟导入。

下面来看一下,插件式如何实现的:

    [ExportPluginAttribute(typeof(IPlugin), ThePluginName = "TheFristPlugin")]
    public class TheFristPlugin : IPlugin
    {
        public TheFristPlugin()
        {
            this.TheName = "TheFristPlugin";
        }
        #region Implementation of IPlugin

        public string TheName { get; set; }

        public void Run()
        {
            MessageBox.Show("TheFristPlugin");
        }

        #endregion
    }

1. 简单说一下:导入与导出之前的关系

一个基于MEF开发的可扩展的程序,在容器中必然有很多的导出(Export),而这些Export又是怎么样找到自己的归宿呢。

Export 与 Import 依靠一种契约,来确定对方是否是自己的兄弟,说白了就是接口,比如上述程序所定义的IPlugin接口

	public interface IPlugin
    {
        void Run();
    }

使用 ExportAttribute 特性导出:

	[Export("count")]
 	public int count{ get{return 0;} }

 	[Export(typeof(Action))]
 	public void SendMsg(){return;}

 	[Export]
 	public class Person{}

有一个需求,主程序要求插件必须要指定插件名称:

1. 在IPlugin接口中定义:Name字段

2. 使用元数据

3. 使用自定义导出特性(与第二种方案类似)

如何使用元数据?

1.定义元数据视图,此处视图使用接口类型

	public interface IPluginMetadata
 	{
     	string ThePluginName { get; }
 	}

2. 导出部件时,使用ExportMetaData特性

	[ExportMetadata("ThePluginName", "TheFivePlugin")]
	[Export(typeof(mef.test.wform.Interface.IPlugin))]
	public class TheFivePlugin : mef.test.wform.Interface.IPlugin
	{
	     public void Run()
	     {
	         MessageBox.Show("TheFivePlugin");
	     }
	}

3. 导入元数据

        [ImportMany(AllowRecomposition = true)]
	private IEnumerable<Lazy<IPlugin, IPluginMetadata>> plugins;

4. 访问元数据

Lazy<T,TMetadata>.Value.Metadata

结束

到此为止,MEF 基本内容已讲解结束,如果有遗漏也请博友留言指出。

文章中很多都是白话,非官方语言,怎么理解的就怎么写,如果有不妥之处,还望各位博友指出。

新年快乐

时间: 2024-11-03 05:35:10

[.NET] 浅谈可扩展性框架:MEF的相关文章

浅谈数据库框架,见笑,请多指正

浅谈数据库框架,见笑,请多指正 http://weibo.com/p/1001603724746155003486 一友说"插件式存储又割裂了SQL引擎的完整逻辑...总体而言在现有框架下MySQL的优化器没有多大改进的价值". 我们且做个技术分析: 1 插件式框架,可以静态/动态加载组件,方便在同类不同属家的模块间切换,这种设计是良好的. 很多软件的设计都采用了"微内核+插件"这样的方式构筑了强大的应用.如Ecplise生态圈. 2 数据库范围内, MySQL的属

2014-07-29 浅谈MVC框架中Razor与ASPX视图引擎

今天是在吾索实习的第15天.随着准备工作的完善,我们小组将逐步开始手机端BBS的开发,而且我们将计划使用MVC框架进行该系统的开发.虽然我们对MVC框架并不是非常熟悉,或许这会降低我们开发该系统的效率,但是我们可以通过边学边做的方式来实现其开发的.这不仅便于我们日后对系统的管理与维护,而且还给我们带来一个学习的动力与实践的地方. 但我们在创建一个基于MVC框架的项目时,就遇到一些问题了.那就是MVC的视图引擎是有两种的,一种是Razor,会以cshtml后缀的文件作为视图文件:另一种是ASPX,

浅谈angular框架

最近新接触了一个js框架angular,这个框架有着诸多特性,最为核心的是:MVVM.模块化.自动化双向数据绑定.语义化标签.依赖注入,以上这些全部都是属于angular特性,虽然说它的功能十分的强大,在开发中非常的实用,对于初接触者来说,要想熟练使用仍然需要一些时间来熟悉其中基础的语法规则,一些基本的用法,虽然说代码不需要死记硬背,但是对于新学习一个知识点,记住其中的一些基础概念,基本的用法,在这里不是提倡大家去背一些代码,该处谈到的熟悉指的是自己多动手去写,实在是想不起了再查看资料,尽量凭借

浅谈spring框架的控制反转和依赖注入

spring是什么? spring是一个轻量级的控制反转和面向切面编程的开源容器框架. 轻量级是说spring框架本身的体积小. 控制反转(Ioc):它不是技术,而是一种思想,将创建对象的控制权力交给spring框架. 依赖注入(DI):将对象中的属性通过配置文件的方式进行赋值. 面向切面编程(AOP):零散存在于业务层中的功能代码(例如:日志,事务),称为横切面关注点.把一个个横切面关注点放到某个模块中,称为切面. AOP就是把多个方法前后的共同代码抽离出来,使用动态代理机制来控制.好处是降低

浅谈MVC框架设计

为什么要设计框架? 在开发的过程中为了方便管理资源,统一一定的规范,所以采用使用框架,为了以后项目的延伸和拓展,比如我们的代码其实都可以在一个方法内进行,而且也不需要相互调用也许会更快吧?但是要是发生错误呢 ?好不容易执行的最后一行结果突然发生错误了,很难受不是  题外话,但是也可以感受到一个好的设计会给我们带来很大的方便 题外话继续 :开发过程中,我们前后台之间相互关联,有一个项目的改动就需要一起协调,这还是一个小功能,要是同时进行一次开发,那就难免深受其累了,怎么样才能方便管理.前后台分离呢

浅谈SpringMVC4 框架技术

为什么要使用SpringMVC 基于MVC架构:(SpringMVC 也叫 Spring web mvc.是 Spring 框架的一部分,是在 Spring3.0 后发布的) 基于MVC架构,功能分工明确,解耦合. 容易理解,上手快,使用简单 开发一个注解的springMVC项目,SpringMVC 也是轻量级的,jar 很小.不依赖的 特定的接口和类. SpringMVC 强化注解的使用,在控制器,Service,Dao 都可以使用注解.方便灵活 作 为 Spring 框 架 一 部 分 ,

浅谈跨平台框架 Flutter 的优势与结构

作者:个推iOS工程师 伊泽瑞尔 一.背景 目前,移动开发技术主要分为原生开发和跨平台开发两种.其中,原生应用是指在某个特定的移动平台上,使用平台所支持的开发工具和语言,直接调用系统提供的API所开发的应用.原生开发的主要优势体现在:1.可以快速访问本平台的全部功能,比如摄像头.GPS等:2.原生应用的速度快.性能高,而且可以实现比较复杂的动画和绘制效果,用户体验较好.原生开发的缺点也很明显,主要体现在:1.开发成本较高,不同的平台必须维护不同的代码,人力成本也会随之增加:2.有新的功能需要更新

浅谈 Flask 框架

一.框架对比 Django Flask Admin —— Model 原生无 Model 原生无 Form 原生无 Session 有 —— 颠覆认知操作 Django —— 教科书式框架 优势:组件全,功能全,教科书 劣势:占用资源,创建复杂度高 Flask —— 以简单为基准开发,一切从简,能省则省 优势:轻,块 劣势:先天不足,第三方组件稳定性较差 二.Flask入门 下载安装 下载:pip install Flask 注意:不要使用工具中的插件创建 Flask 项目 三行代码启动Flas

浅谈 Web框架

一.Web框架本质 所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端 二.Web框架功能 socket收发消息 —— wsgiref(测试).uwsgi(线上) 根据不同的路径返回不同的字符串 返回动态页面(字符串的替换)—— jinja2 三.Web框架种类 django 根据不同的路径返回不同的字符串 返回动态页面(字符串的替换) flask 根据不同的路径返回不同的字符串 tornado socket收发消息 根据不同的路径返回不同的字符串 返回动