65.9K
CodeProject 正在变化。 阅读更多。
Home

MEF化应用程序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.62/5 (10投票s)

2008 年 12 月 8 日

CPOL

4分钟阅读

viewsIcon

47826

downloadIcon

1255

一个非常简单的示例,说明如何使用 MEF 使现有应用程序可扩展

引言

你最喜欢的程序是什么?Office、Visual Studio、Photoshop、World of Warcraft、Reflector?

它们有什么共同点?如今最流行的软件中,有99%在某种程度上是可扩展的。当然,要流行不一定非得可扩展吧?

你目前正在编写的软件是否可扩展?如果不是,为什么?

过去,实现可扩展性非常困难,你必须依靠反射自己完成所有工作!并非不可能,但随着时间的推移,你会发现安全、隔离、跨越应用程序边界和生命周期管理自己实现起来非常困难!微软还发布了 System.AddIn (Managed AddIn Framework)。MAF 在你理解了管道之后,确实让事情变得稍微容易一些……但仍然太难轻松地添加到你的应用程序中……

Extensibility.JPG

介绍 Microsoft Extensibility Framework

Managed Extensibility Framework (MEF) 是 .NET 中的一个新库,它能够更广泛地重用应用程序和组件。使用 MEF,.NET 应用程序可以从静态编译转向动态组合。如果你正在构建可扩展的应用程序、可扩展的框架和应用程序扩展,那么 MEF 就是为你准备的。

开始吧

我创建了一个非常简单的 Window Live Writer 克隆。Live Writer 有大量的插件可用。每个插件都应该能够向文档添加内容。

RudiGrobler.Writter.jpg

契约

可组合部件(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 化

  1. 在我们开始使用 MEF 之前,请引用System.ComponentModel.Composition.dll(MEF 预览版 3)。

  2. Export属性装饰你的插件

    [Export(typeof(RudiGrobler.Writter.Common.IContentSource))]
  3. 为插件使用一个通用目录(可选)

    path.JPG

就是这样……非常简单!

字符串 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 开箱即用提供了一些目录……我们将使用DirectoryPartCatalogDirectoryPartCatalog会扫描指定的目录(扩展)以查找所有可用的部件。

CompositionContainer container = new CompositionContainer(catalog);
container.AddPart(this);
container.Compose();

容器顾名思义!它是一个包含可组合部件的容器。容器与 Unity 等 IOC/DI 容器非常相似。调用容器的Compose方法会将所有导出的部件(拥有)与导入的部件(需要)进行匹配。

Easy.jpg

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

PluginsFound.jpg

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

博客

© . All rights reserved.