MEF 編程指南(六):導出和元數據

聲明導出解釋了部件導出服務的基礎知識和價值觀(Values)。有時候出於種種緣由,導出關聯信息是很是必要的。一般,用於解釋關於功能公共契約的具體實現。容許導入知足約束要求的導出,或者導入全部可用的實現而且在導出前在運行時檢查他們的功能。html

 
爲導出附加元數據(Attaching Metadata to an Export)
 
考慮到 IMessageSender 服務更早引入。假設咱們有一些實現,和相關的消費者實現(Consumer Of The Implementations)有差別。對於咱們的示例,消息的傳送(Transport)和是否安全(Secure)對於消費者(導入部件)都是重要的信息。
 
 
使用 ExportMetadataAttribute 特性
 
全部咱們須要作的事情是使用 [System.ComponentModel.Composition.ExportMetadataAtrribute] 附加這些信息。
 
    [Export(typeof(IMessageSender))]
    [ExportMetadata("transport", "smtp")]
    public class EmailSender : IMessageSender
    {
        public void Send(string message)
        {
            Console.WriteLine(message);
        }
    }
 
    [Export(typeof(IMessageSender))]
    [ExportMetadata("transport", "smtp")]
    [ExportMetadata("secure", null)]
    public class SecureEmailSender : IMessageSender
    {
        public void Send(string message)
        {
            Console.WriteLine(message);
        }
    }
 
    [Export(typeof(IMessageSender))]
    [ExportMetadata("transport", "phone_network")]
    public class SMSSender : IMessageSender
    {
        public void Send(string message)
        {
            Console.WriteLine(message);
        }
    }
 
    public interface IMessageSender
    {
        void Send(string message);
    }

 

 
 
使用自定義導出特性(Using a Custom Export Attribute)
 
爲了使用比 ExportMetadata 更強類型,須要建立自定義特性而且用 [System.ComponentModel.Composition.MetadataAttribute] 標識。本例中也繼承自 ExportAttribute 特性,於是建立自定義導出特性而且指定了元數據。
 
 
    [MetadataAttribute]
    [AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
    public class MessageSenderAttribute : ExportAttribute
    {
        public MessageSenderAttribute() : base(typeof(IMessageSender)) { }
        public MessageTransport Transport { get; set; }
        public bool IsSecure { get; set; }
    }
 
    public enum MessageTransport
    {
        Undefined,
        Smtp,
        PhoneNetwork,
        Other
    }
 
    public interface IMessageSender
    {
        void Send(string message);
    }

 

 
以上,MetadataAttribute 應用於自定義導出特性。下一步是將特性應用 IMessageSender 實現:
 
    [MessageSender(Transport=MessageTransport.Smtp)]
    public class EmailSender : IMessageSender
    {
        public void Send(string message)
        {
            Console.WriteLine(message);
        }
    }
 
    [MessageSender(Transport=MessageTransport.Smtp, IsSecure=true)]
    public class SecureEmailSender : IMessageSender
    {
        public void Send(string message)
        {
            Console.WriteLine(message);
        }
    }
 
    [MessageSender(Transport=MessageTransport.PhoneNetwork)]
    public class SMSSender : IMessageSender
    {
        public void Send(string message)
        {
            Console.WriteLine(message);
        }
    }

 

 

這就是在導出端須要作的所有事情。MEF 機制下依然會填充一個字典(Populating A Dictionary),可是這對用戶不可見。
 
注意:你也能夠建立沒有自身導出的元數據特性,經過建立一個使用 MetadataAttribute 標識的特性。在這些狀況下,元數據會被添加到有相同成員的應用自定義元數據特性的導出。
 
 
導入元數據(Importing Metadata)
 
導入部件能訪問附加到導出的元數據。
 
 
使用強類型元數據(Using Strongly-typed Metadata)
 
 
爲了訪問強類型的元數據,經過定義匹配只讀屬性(名稱和類型)的接口建立元數據視圖。對於咱們示例,像下面的接口:
 
    public interface IMessageSenderCapabilities
    {
        MessageTransport Transport { get; }
        bool IsSecure { get; }
    }

 

 
而後,就可使用 System.Lazy<T, TMetadata> 類型開始導入,其中 T 是契約類型,TMetadata 是建立的接口。
 
 
    public class HttpServerHealthMonitor
    {
        [ImportMany]
        public Lazy<IMessageSender, IMessageSenderCapabilities>[] Senders { get; set; }
 
        public void SendNotification()
        {
            Compose();
            foreach (var sender in Senders)
            {
                if (sender.Metadata.Transport == MessageTransport.Smtp && sender.Metadata.IsSecure)
                {
                    var messageSender = sender.Value;
                    messageSender.Send("Server is fine");
 
                    break;
                }
            }
        }
 
        private void Compose()
        {
            //var container = new CompositionContainer();
            //container.ComposeParts(this, new EmailSender());
            AssemblyCatalog catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
            var container = new CompositionContainer(catalog);
            container.ComposeParts(this);
        }
    }

 

使用弱類型元數據(Using Weakly-Typed Metadata)
 
爲了用弱類型的方式訪問元數據,使用 System.Lazy<T, TMetadata> 類型傳遞 IDictionary<string, object> 元數據給導入。而後,能夠經過 Dictionary 的元數據屬性訪問元數據。
 
注意:一般,咱們建議經過強類型方法訪問元數據,而後有些系統須要容許以動態形式訪問元數據。
 
 
    public class HttpServerHealthMonitor
    {
        [ImportMany]
        public Lazy<IMessageSender, IDictionary<string, object>>[] Senders { get; set; }
 
        public void SendNotification()
        {
            Compose();
 
            foreach (var sender in Senders)
            {
                if (sender.Metadata.ContainsKey("Transport"))
                {
                    var messageSender = sender.Value;
                    messageSender.Send("Server is fine");
                }
            }
        }
 
        private void Compose()
        {
            //var container = new CompositionContainer();
            //container.ComposeParts(this, new EmailSender());
            AssemblyCatalog catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
            var container = new CompositionContainer(catalog);
            container.ComposeParts(this);
        }
    }

 

 
元數據過濾和 DefaultValueAttribute 特性(Metadata Filtering and DefaultValueAttribute)
 
 
當指定元數據視圖(Metadata View)的時候,隱式過濾將會匹配那些在視圖定義中知足包含元數據屬性條件的導出。你可使用 System.ComponentModel.DefaultValueAttribute 特性指定一個元數據視圖是非必要的,下面你能夠看到,咱們在 IsSecure 上指定默認值(Default Value)爲假(False)。這意味着,若是一個部件導出 IMessageSender,可是沒有提供 IsSecure 元數據,也仍將被匹配。
 
    public interface IMessageSenderCapabilities
    {
        MessageTransport Transport { get; }
        [DefaultValue(false)]
        bool IsSecure { get; }
    }

 

 
原文地址:
相關文章
相關標籤/搜索