如果不使用MEF进行托管扩展处理,只有通过WebClient进行程序包的下载、解析。实际上MEF的动态下载的底层实现一样是使用的WebClient,然后利用AggregateCatalog进行动态组合,详细可查看MEF的源代码(路径:Composition.Initialization\System\ComponentModel\Composition\Hosting\DeploymentCatalog.cs)。
在上一篇程序设计指南《MEF程序设计指南六:MEF中的目录服务(DeploymentCatalog)》中介绍了MEF的目录服务,并对MEF的目录服务进行了接口封装,其中有一个接口就是专门封装的使用MEF的目录进行.xap程序包的动态装载的。
{
DeploymentCatalog catalog;
if ( ! _catalogs.TryGetValue(relativeUri, out catalog))
{
catalog = new DeploymentCatalog(relativeUri);
if (completedAction != null )
catalog.DownloadCompleted += (s, e) => completedAction(e);
else
catalog.DownloadCompleted += DownloadCompleted;
catalog.DownloadAsync();
_catalogs[relativeUri] = catalog;
_aggregateCatalog.Catalogs.Add(catalog);
}
}
其应用也非常简单,通过MEF的导入将接口导入到需要使用的地方,然后直接调用上面的方法即可实现对指定路径的xap包的动态下载以及组合。
public IDeploymentService Service { get ; set ; }
private void button1_Click( object sender, System.Windows.RoutedEventArgs e)
{
this .Service.AddXap( " MEFTraining.MefCatalogs.Parts.xap " , null );
}
到这里我们还需要学习另外一个接口的使用,IPartImportsSatisfiedNotification接口就是一个当有新的部件进行装配成功后的一个通知接口,可以准确的监听到MEF容器的组合,一旦有新的插件部件进行导入装载到MEF容器中,此接口就会自动的得到通知。其内部就一个接口方法,详细如下代码块:
{
}
在使用MEF目录进行导出部件托管的时候,在某些需求下或许只需要其中的一个部件,这种情况可以通过遍历部件集合得到。然而MEF也为此提供了一种解决方案,那就是使用目录过滤筛选功能。MEF中的ComposablePartCatalog类和INotifyComposablePartCatalogChanged接口就是专门用来实现目录筛选的,可以如下代码段中演示的对目录过滤的封装。
{
private readonly ComposablePartCatalog _inner;
private readonly INotifyComposablePartCatalogChanged _innerNotifyChange;
private readonly IQueryable < ComposablePartDefinition > _partsQuery;
public FilteredCatalog(ComposablePartCatalog inner,
Expression < Func < ComposablePartDefinition, bool >> expression)
{
_inner = inner;
_innerNotifyChange = inner as INotifyComposablePartCatalogChanged;
_partsQuery = inner.Parts.Where(expression);
}
public event EventHandler < ComposablePartCatalogChangeEventArgs > Changed
{
add
{
if (_innerNotifyChange != null )
_innerNotifyChange.Changed += value;
}
remove
{
if (_innerNotifyChange != null )
_innerNotifyChange.Changed -= value;
}
}
public event EventHandler < ComposablePartCatalogChangeEventArgs > Changing
{
add
{
if (_innerNotifyChange != null )
_innerNotifyChange.Changing += value;
}
remove
{
if (_innerNotifyChange != null )
_innerNotifyChange.Changing -= value;
}
}
public override System.Linq.IQueryable < ComposablePartDefinition > Parts
{
get
{
return _partsQuery;
}
}
}
通过上面的封装,使用目录过滤功能之需要传入正确的筛选表达式就可以了,按照MEF中的约定其筛选表达式应如下格式。
def => def.Metadata.ContainsKey(CompositionConstants.PartCreationPolicyMetadataName) &&
((CreationPolicy)def.Metadata[CompositionConstants.PartCreationPolicyMetadataName]) == CreationPolicy.NonShared);
MEF中提供了一个专门用于目录过滤筛选的元数据特性PartMetadata,要进行目录部件的筛选过滤就需要通过PartMetadata特性的标注,MEF容器才能进行正确的装配。以一个简单的实例演示,比如说有三个用户控件(UserControl),分别进行导入和筛选过滤元数据的配置。
[Export( typeof (UserControl))]
public partial class AA : UserControl
{
public AA()
{
InitializeComponent();
}
}
[PartMetadata( " UC " , " BB " )]
[Export( typeof (UserControl))]
public partial class BB : UserControl
{
public BB()
{
InitializeComponent();
}
}
[PartMetadata( " UC " , " CC " )]
[Export( typeof (UserControl))]
public partial class CC : UserControl
{
public CC()
{
InitializeComponent();
}
}
三个用户控件的元数据名称都为"UC",其值分别是AA、BB、CC,那么就可以通过下面代码的方式实现对目录中部件的筛选,下面是代码块演示了如何从目录中筛选出元数据名称为"UC",其值为"CC"的部件。
var catalog = new AssemblyCatalog( typeof (MainPage).Assembly);
// 将目录装载进MEF组合容器
var parent = new CompositionContainer(catalog);
// 通过元数据过滤筛选出元数据名称为"UC"值为"CC"的组合部件
var filteredCat = new FilteredCatalog(catalog,
def => def.Metadata.ContainsKey( " UC " ) &&
def.Metadata[ " UC " ].ToString() == " CC " );
var perRequest = new CompositionContainer(filteredCat, parent);
var control = perRequest.GetExportedValue < UserControl > ();