MEF初体验之八:过滤目录
当在使用子容器的时候,基于某些具体标准来过滤目录可能是重要的。例如,基于部件的创建策略来过滤是很常见的。下面的代码片段演示了如何构建这种特别方法:
var catalog = new AssemblyCatalog(typeof(Program).Assembly); var parent = new CompositionContainer(catalog); var filteredCat = new FilteredCatalog(catalog, def => def.Metadata.ContainsKey(CompositionConstants.PartCreationPolicyMetadataName) && ((CreationPolicy)def.Metadata[CompositionConstants.PartCreationPolicyMetadataName]) == CreationPolicy.NonShared); var child = new CompositionContainer(filteredCat, parent); var root = child.GetExportedObject<Root>(); child.Dispose();
如果CreationPolicy还足以作为一个标准来选择部件的话,你或许想使用[PartMetadata]来代替。它允许你附加元数据在部件上,因此你可以使用它来构建一个过滤表达式。例如,下面是一个应用该特性的类:
[PartMetadata("scope", "webrequest"), Export] public class HomeController : Controller { }
这使你可以用那些应该被限定到一个(逻辑)web请求的部件来创建子容器。注意它由你来定义一个范围边界,换句话说,MEF并不知道"webrequest"是什么,因此,你必须在每次web请求时建立一些基础设施代码来创建/释放容器。
var catalog = new AssemblyCatalog(typeof(Program).Assembly); var parent = new CompositionContainer(catalog); var filteredCat = new FilteredCatalog(catalog, def => def.Metadata.ContainsKey("scope") && def.Metadata["scope"].ToString() == "webrequest"); var perRequest = new CompositionContainer(filteredCat, parent); var controller = perRequest.GetExportedObject<HomeController>(); perRequest.Dispose();
注意:我们没有提供FilteredCatalog类。下面我们将拿一个简单的实现来说明创建FilteredCatalog:
using System; using System.ComponentModel.Composition.Primitives; using System.ComponentModel.Composition.Hosting; using System.Linq; using System.Linq.Expressions; public class FilteredCatalog : 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 override IQueryable<ComposablePartDefinition> Parts { get { return _partsQuery; } } 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; } } }
最后举个简单例子:
using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace FilteringCatalogsSample { class Program { [ImportMany] public IEnumerable<IMessageSender> Senders { get; set; } static void Main(string[] args) { Program p = new Program(); p.Compose(); foreach (var item in p.Senders) { item.Send("Hi,MEF"); } Console.ReadKey(); } void Compose() { var catalog = new AssemblyCatalog(typeof(Program).Assembly); var parent = new CompositionContainer(catalog); var filterCatalog = new FilteredCatalog(catalog, def => def.Metadata.ContainsKey("methods") && def.Metadata["methods"].ToString() == "sms"); //var AggrContainer = new CompositionContainer(filterCatalog, parent); var child = new CompositionContainer(filterCatalog); child.ComposeParts(this); } } interface IMessageSender { void Send(string msg); } [Export(typeof(IMessageSender))] public class EmailSender : IMessageSender { public void Send(string msg) { Console.WriteLine("Email sent:" + msg); } } [Export(typeof(IMessageSender))] public class SecureEmailSender : IMessageSender { public void Send(string msg) { Console.WriteLine("Secure Email sent:" + msg); } } [Export(typeof(IMessageSender))] [PartMetadata("methods","sms")] public class SMSSender : IMessageSender { public void Send(string msg) { Console.WriteLine("SMS sent:" + msg); } } }
输出如图: