MEF化应用程序






4.62/5 (10投票s)
一个非常简单的示例,说明如何使用 MEF 使现有应用程序可扩展
引言
你最喜欢的程序是什么?Office、Visual Studio、Photoshop、World of Warcraft、Reflector?
它们有什么共同点?如今最流行的软件中,有99%在某种程度上是可扩展的。当然,要流行不一定非得可扩展吧?
你目前正在编写的软件是否可扩展?如果不是,为什么?
过去,实现可扩展性非常困难,你必须依靠反射自己完成所有工作!并非不可能,但随着时间的推移,你会发现安全、隔离、跨越应用程序边界和生命周期管理自己实现起来非常困难!微软还发布了 System.AddIn (Managed AddIn Framework)。MAF 在你理解了管道之后,确实让事情变得稍微容易一些……但仍然太难轻松地添加到你的应用程序中……

介绍 Microsoft Extensibility Framework
Managed Extensibility Framework (MEF) 是 .NET 中的一个新库,它能够更广泛地重用应用程序和组件。使用 MEF,.NET 应用程序可以从静态编译转向动态组合。如果你正在构建可扩展的应用程序、可扩展的框架和应用程序扩展,那么 MEF 就是为你准备的。
开始吧
我创建了一个非常简单的 Window Live Writer 克隆。Live Writer 有大量的插件可用。每个插件都应该能够向文档添加内容。

契约
可组合部件(Composable Parts)之间不直接相互依赖,它们依赖于一个契约(contract),这是一个string
标识符。每个导出(export)都有一个契约,每个导入(import)都声明了它需要的契约。容器使用契约信息来匹配导入和导出。如果没有指定契约,MEF将隐式使用类型的完全限定名作为契约。如果传递了一个类型,它也将使用完全限定名。
public interface IContentSource
{
string FriendlyName { get; set; }
Nullable<bool> CreateContent(System.Windows.Window owner, FlowDocument document);
}
每个插件(可组合部件)都应该有一个FriendlyName
,并通过调用CreateContent
将内容添加到FlowDocument
中。
可组合部件
可组合部件是 MEF 中的一个可组合单元。可组合部件导出其他可组合部件需要的服务,并导入其他可组合部件的服务。在 MEF 编程模型中,可组合部件通过System.ComponentModel.Composition.Import
和[System.ComponentModel.Composition.Export]
属性进行修饰,以声明它们的导出和导入。一个可组合部件应该至少包含一个导出。可组合部件要么被显式添加到容器中,要么通过使用目录(catalogs)创建。MEF 默认提供的目录通过导出属性的存在来识别可组合部件。
public class InsertHelloWorld : RudiGrobler.Writter.Common.IContentSource
{
public string FriendlyName { get; set; }
public InsertHelloWorld()
{
FriendlyName = "Insert Hello World...";
}
public bool? CreateContent(System.Windows.Window owner, FlowDocument document)
{
document.Blocks.Add(new Paragraph(new Run("Hello World")));
return true;
}
}
这是一个非常基础的插件,它只将“Hello World”插入到FlowDocument
中。
MEF化应用程序
目前我们的应用程序是静态编译的。这行得通……但是现在我想添加插件?更糟糕的是……如果你想添加一个插件怎么办?
只需要对你的插件进行 3 处更改即可 MEF 化
-
在我们开始使用 MEF 之前,请引用System.ComponentModel.Composition.dll(MEF 预览版 3)。
-
用
Export
属性装饰你的插件[Export(typeof(RudiGrobler.Writter.Common.IContentSource))]
-
为插件使用一个通用目录(可选)
就是这样……非常简单!
字符串 vs 类型?
在典型的基于反射的插件框架中,contracts
类型用于查找所有插件!如果你有一个static
语言(如 C#),这没问题,但如果你想使用 IronPython 开发插件呢?或者任何其他动态语言?MEF 尝试通过从传统的基于类型标识转向简单的string
标识符来适应这些语言!
现在我们有了一些符合 MEF 标准的插件……我们如何找到并使用它们?
目录 & 容器
目录负责发现扩展,而容器则协调创建和满足依赖关系。
加载可组合部件有很多方法……我将使用Import
属性
[Import]
public IEnumerable<rudigrobler.writter.common.icontentsource> Extensions { get; set; }
MEF 的属性编程模型的价值主张之一是通过目录动态发现导出的能力。目录允许应用程序轻松地使用通过Export
属性自我注册的导出。下面是 MEF 默认提供的目录列表
ComposablePartCatalog catalog = new DirectoryPartCatalog("extensions", true);
MEF 开箱即用提供了一些目录……我们将使用DirectoryPartCatalog
!DirectoryPartCatalog
会扫描指定的目录(扩展)以查找所有可用的部件。
CompositionContainer container = new CompositionContainer(catalog);
container.AddPart(this);
container.Compose();
容器顾名思义!它是一个包含可组合部件的容器。容器与 Unity 等 IOC/DI 容器非常相似。调用容器的Compose
方法会将所有导出的部件(拥有)与导入的部件(需要)进行匹配。

这就是让你的应用程序可扩展所需的所有内容!这是找到的插件列表

MEF 在将软件的可扩展性映射到我们的逻辑思维方式方面做得非常出色:我拥有(导出),你需要(导入)。