MEF: Managed Extensibility Framework 可以很容的构造可扩展性的应用程序。MEF提供了发现和组合能力,因此你可以选择来加载插件。
MEF解决了什么问题?
- MEF赠送了一种简单的在运行时扩展问题。直到现在,任何程序想支持插件模式,需要构建自己的构架。这些插件经常是特定应用的并且不能被多种实现重用的。
- MEF提供了一个标准方式来让程序暴露自己,消耗外部扩展。扩展,是天生的,能被不同的程序重用。然后,一个扩展需要实现特定应用。扩展可以依赖另一个扩展,MEF确保他们以正确的顺序同时被装配(插件的插件)。
- MEF允许在查询和筛选中使用额外的元数据来标记扩展。
核心机制图:
关键词: 导入;导出;装配;提供者;消费者
Export:提供者属性标记
- [Export]
- [Export(Type)]
- [Export(string,Type)]
Import:消费者标示
他们通过CompositionEngine来实现自动的组装。
MEF工作方式的要点:
- MEF的核心由一个Catalog(目录)和一个CompositionContainer组成。Catalog负责查找扩展;Contaner负责协调创建和符合的依赖。
- MEF第一个类是ComposablePart。一个ComposablePart提供了一个或多个Exports,也可以依赖于一个或多个由Imports提供的扩展。一个ComposablePart管理可以是Object或给定类型的实例。
- Exports和Imports每个都有一个契约(Contract)。契约(Contract)是Exports和Imports之间的桥梁。一个Export契约由用来过滤的元数据组成。
- 默认MEF Composable Part实现使用Attribute元数据来声明Exports和Imports。这允许MEF来决定哪个Parts,Imports和Exports是完全可用的。
MEF在什么地方:C:\Program Files (x86)\Microsoft SDKs\Silverlight\v4.0\Libraries\Client
MEF DEMO讲解
.NET 4.0中提供了一个MEF框架用于开发支持插件的软件系统,其中Silverlight4也支持MEF。我们的DEMO要实现的功能如下:
用户在访问Silverlight应用程序时,开始只显示一个“初始的简单的”页面,当用户需要时,动态的从Web网站上下载新的程序集,之后,Silverlight客户端应用程序在使用MEF将动态下载的程序集中所包含的页面组件“组装”成一个新的功能增强的页面。
1. 新建一个DynamicComposePage的Silverlight Bsiness Application项目。生成的项目中可以看到\DynamicComposePage\DynamicComposePage\Views\Home.xaml文件,等一会我们将修改此页面实现页面的动态装配。
2. 现在我们需要提供一个MEF部件都遵循的接口,为此,向解决方案添加一个“SIlverlight类库”项目:MyPartCotract,并向该项目中添加一个IMyPart接口,为了简单起见,本例不为此添加任何成员,当然了,在实际开发中国可以依据需要为其添加合适的成员。
namespace MyPartContract
{
public interface IMyPart
{
}
}
注意:这不是普通的“类库”项目,Silverlight所使用的程序集是重新编写的,与标准的.NET Framework不一样。
3. 下面定义可供动态组合的Silverlight页面组件。
向解决方案中添加一个名为“MyPageParts”的“Silverlight类库”项目,注意在弹出的对话框中要选择为“Silverlight 4”。
然后向MyPagePart项目中添加一个“Silverlight User Control”,取名为“MyEditorControl”,在这个页面中我们放置一个“RichTextArea”控件来充当文字编辑器:
<UserControl x:Class="MyPageParts.MyEditorControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<RichTextBox x:Name="RichTextBox1" Height="100" Width="100"/>
</Grid>
</UserControl>
现在,需要让此用户控件可以被MEF动态装配,为此,需要完成两个步骤:
(1) 给本项目(也包括前面创建的Silverlight项目DynamicComposePage)都添加对包容了IMyPart接口的Silverlight类库MyPartContract的引用。
(2) 给本项目(也包括前面创建的Silverlight项目DynamicComposePage)都添加对MEF核心程序集System.ComponentModel.Composition.dll的引用,将要负责完成“装配”工作的Silverlight项目DynamicComposePage还需要添加对另一个核心程序集System.ComonentModel.Composition.Initialization.dl的引用。
注意引用类库的位置:C:\Program Files (x86)\Microsoft SDKs\Silverlight\v4.0\Libraries\Client
(3) 接着是要在支持MEF动态装配的Silverlight 4用户控件的相应代码上,进行Export导出操作:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel.Composition;
using MyPartContract;
namespace MyPageParts
{
[Export(typeof(IMyPart))] //Export标记,Contract类型为IMyPart类型
public partial class MyEditorControl : UserControl,IMyPart //由于Export时的type是IMyPart,所以这里也有实现IMyPart接口
{
public MyEditorControl()
{
InitializeComponent();
}
}
}
(4) 现在开发进行“动态装配”的Silverlight页面(位于DynamicComposePage项目Views文件夹中的Home.xaml)。
在页面上放置一个Button用于启动装配工程,另一个TextBlok用于显示提示信息,更重要的是放置一个ContentControl作为部件容器,用于显示动态装配出来的页面部件:
<navigation:Page
x:Class="DynamicComposePage.Home"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480"
Style="{StaticResource PageStyle}">
<Grid x:Name="LayoutRoot">
<ScrollViewer x:Name="PageScrollViewer" Style="{StaticResource PageScrollViewerStyle}">
<StackPanel x:Name="ContentStackPanel" Style="{StaticResource ContentStackPanelStyle}">
<TextBlock x:Name="HeaderText" Style="{StaticResource HeaderTextStyle}"
Text="{Binding Path=Strings.HomePageTitle, Source={StaticResource ApplicationResources}}"/>
<Button x:Name="btnShowEditor" Click="btnShowEditor_Click" Height="20" Width="50" Content="ShowEditor"/>
<TextBlock HorizontalAlignment="Center" Margin="5" x:Name="txtInfo" Text="说明:点击按钮显示文本编辑器..." FontSize="15"/>
<ContentControl x:Name="MyEditorContainer" Height="200" Width="300"/>
</StackPanel>
</ScrollViewer>
</Grid>
</navigation:Page>
下面简单介绍一下页面中的关键代码:
首先,我们需要制定Home.xaml页面“需要”一个IMyPart部件,因为,我们给Home类添加以下属性,并且给其附加“[Import]”标记:
[Import(typeof(IMyPart))]
public IMyPart myPart { get; set; }
在Home.xaml页面初次显示时,并不加载部件程序集,当用户点击“ShowEditor”按钮后,动态创建一个WebClient对象从Web网站上下载程序集:
//动态下载程序集并装载
private void DownloadAssemblyAndCompose()
{
//获取程序集的URI
string uri = Application.Current.Host.Source.AbsoluteUri;
int index = uri.IndexOf("/ClientBin");//找出根URL
uri = uri.Substring(0, index) + "/MyParts/MyPageParts.dll";
WebClient wClient = new WebClient();
this.txtInfo.Text = "正在下载文本编辑器组件...";
wClient.OpenReadCompleted += new OpenReadCompletedEventHandler(wClient_OpenReadCompleted);
//启动异步下载
wClient.OpenReadAsync(new Uri(uri));
this.btnShowEditor.IsEnabled = false; //防止用户二次启动下载
}
注意:在本实例中我们假设所有的可装配部件都放在Web网站的MyParts文件夹下,并且假设我们已经知道了要下载的程序集文件名。在实际项目中,我们可以设计一个用于扫描内部文件夹并向Silverlight客户端返回可装配部件的WCF Service,以允许真正“全动态”的装配工作。
上面代码为WebClient的下载完毕事件(OpenReadCompletedEvent)挂接了一个事件响应方法,其中包容了本例最核心的功能代码:
void wClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
try
{
//加载资源
AssemblyPart part = new AssemblyPart();
Assembly ass = part.Load(e.Result);
//创建Catalog
AssemblyCatalog cata = new AssemblyCatalog(ass);
CompositionContainer container = new CompositionContainer(cata);
CompositionBatch bat = new CompositionBatch();
bat.AddPart(this);
container.Compose(bat); //装配
//显示装配好的页面组件
if (myPart != null)
{
MyEditorContainer.Content = myPart;
this.txtInfo.Text = "下载结束,现在您可以编写文档了";
}
}
catch (Exception ex)
{
txtInfo.Text = ex.Message;
btnShowEditor.IsEnabled = true; //如果有异常,允许用户再次尝试加载
}
}
上述代码中的关键在于下载程序集完成之后,从流中动态加载陈续集,然后再调用MEF来装配部件。
5. 在Web网站中常见一个专用的部件文件夹MyParts,将包容了可装配部件的程序集赋值到这一文件夹下。先编译一个该项目,然后把\DynamicComposePage\MyPageParts\Bin\Debug\MyPageParts.dll编译好的dll文件复制到MyParts里。
运行后的结果如下:
从本实例看到,将MEF和WebClient组件结合起来,可以让我们实现Silverlight页面的“按需下载”和“动态组合”。