Pull to refresh

Managed Extensibility Framework

Reading time 7 min
Views 14K
(Заранее извините за мой русский)

MEF это новый подход разработанный Майкрософт, который позволяет загружать расширения для вашего приложения очень легко. Он позволяет обнаруживать и составлять части, которые должны быть включены в приложение, и все это во время выполнения. Вы можете расширить поведение аппликации простим добавлением новых плагинов. Managed Extensibility Framework сделает все это за вас.
«Hello MEF World!»

Допустим, мы уже имеем действительно простое приложение, и мы хотим, чтоб оно приветствовало нас с "Привет MEF мир!":



    class Program
    {
        static void Main(string[] args)
        {
            var program = new Program();
            program.Run();
        }

        private void Run()
        {
            ProgrammGreeter.SayHello();
        }

        protected IGreeter ProgrammGreeter{ get; set;}
    }


    public interface IGreeter
    {
        void SayHello();
    }

    public class Greeter:IGreeter
    {
        public void SayHello()
        {
            Console.WriteLine(«Hello MEF World!»);
        }
    }
Основная проблема заключается в имении инстанса в проперте ProgrammGreeter. У нас есть желание, чтоб это бил инстанс класса Greeter. 
Давайте сделаем это с помощью MEF

Для этого нужно включить ссылку на System.ComponentModel.Composition.

Соответственно с MSDN: «MEF является неотъемлемой частью. NET Framework 4 Beta 1, и используется везде, где может NET Framework используется».

Скорее всего, MEF будет включен в NET Framework 4.0, пока можно скачать его с сайта CodePlex здесь


После того как мы добавили ссылку можно добавить импорт (Import) и экспорт (Export) атрибуты. 
Экспорт предназначен для указания на некоторые возможности, которые ми хотим где-то использовать. И импорт выступает за указание зависимостей от каких-либо возможностей. 

Наш класс Greeter предоставляет некоторые возможности, таким образом, мы добавляем Export:

    [Export(typeof(IGreeter))]
    public class Greeter : IGreeter

Мы хотим использовать эту возможность в нашем классе Programm, т.е. мы зависим от этой функциональности. Добавляем Import:

        [Import]
        protected IGreeter ProgrammGreeter { get; set; }

Обычно наши возможности живут в других асемблях. Но также они могут жить и в вызываемой ассембли. В любом случае мы должны указать MEF, как сконфигировать все наши зависимости. 
Это может быть сделано с помощью метода Compose() которой мы вызовем прежде, чем мы будем использовать наши возможности, как правило такой сетап делается где-то в начале исполнения: 

        private void Run()
        {
            Compose();
            ProgrammGreeter.SayHello();
        }

        private void Compose()
        {
            var assemblyCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
            var compositionContainer = new CompositionContainer(assemblyCatalog);
            compositionContainer.ComposeParts(this);
        }


Я запустил мою программу и получил:



Как работает Managed Extensibility Framework? 

Я думаю, что вы несколько заинтересовались методом Compose(). Просто прочтите комментарии в коде этого метода, я сделал метод более подробным:

        //We have to guide MEF how to Compose all we need
        private void Compose()
        {
            //Catalog says where to search for capabilities (Where do exports live)
            var assemblyCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
            //CompositionContainer holds defined dependencies and coordinates creation
            var compositionContainer = new CompositionContainer(assemblyCatalog);

            //CompositionBatch object holds references to all objects that need to be composed
            var compositionBatch = new CompositionBatch();
            //one of such objects is Programm instance (this), it needs to be composed
            compositionBatch.AddPart(this);

            //And finally Container has method called Compose to do actuall Composing
            compositionContainer.Compose(compositionBatch);
        }



В Managed Extensibility Framework есть примитивы называемее ComposablePart, которые содержат расширения для вашего приложения собрание из каталога (Catalog). Но также он может содержать зависимости, которые также нуждаются в компоновке. 

Посмотрите на этот график со страницы Managed Extensibility Framework. 



Более сложный пример 

В этом примере мы будем иметь приложение, которое анализирует XML-файлы (которые содержит информацию о некоторых разработчиках). После того как мы поставили нашу программу Клиентам, мы хотим, чтобы этот XML поддавался проверке некоторых правил, но они могут отличаться от клиента к клиенту. Для этого разрабатываемо правила в отдельных Dll, которые поставляются различным клиентам. Работаем с правилами как с плагинами. 


У нас есть две сборки:
  • MEF.DEMO которая определяет ISecurityRule и IDataRule.
  • MEF.Rules имеет имплементацию этих интерфейсов.


Структура классов как показано на картинке ниже. Я просто не хочу утомлять вас с объяснением логики всех правил. (Если вам нужны сорсы просто дайте мне знать.)



Наш класс Programm хочет загружать правила из другой сборки, поэтому мы отмечаемо их с помощью Import в MEF.Rules:

        [Import]
        public ISecurityRule SecurityRule { get; set; }

        [ImportMany]
        public IDataRule[] DataRules { get; set; }

        public void Run()
        {
            Console.WriteLine(«Programm run.»);
            Compose();
            Console.WriteLine(«Composition completed.»);

            var document = XDocument.Load(«developer.xml»);
            Console.WriteLine(document.ToString());

            var passesValidation = SecurityRule.PassesValidation(document);
            Console.WriteLine(string.Format(«Rule {0}: {1}», SecurityRule.GetType(), passesValidation));

            foreach (var d in DataRules)
            {
                var valid = d.IsValid(document);
                Console.WriteLine(string.Format(«Rule {0}: {1}», d.GetType(), valid));
            }          
        }

Как вы заметили, мы используем ImportMany, надеюсь что название говорит само за себя. 

Мы должны изменить наш метод компоновки Compose для того чтоб он искал Export-с в директории где наше приложение расположено, для этого мы используем DirectoryCatalog.

        private void Compose()
        {
            var catalog = new DirectoryCatalog(Environment.CurrentDirectory);
            var container = new CompositionContainer(catalog);
            container.ComposeParts(this);
        }


После того как я положил эти DLL в одной папке и запустил MEF.DEMO, я получил:



Целью MEF является беспрепятственно склеить классы, которые импортируют функционал из других частей приложения, а также классы, которые экспортируют этот функционал. Пожалуй вы решите что MEF еще один DI контейнер. Да, он может все те же что и DI контейнеры, но Managed Extensibility Framework в первую очередь сосредоточен на композиции. Также важно, что композиция может быть сделана во время выполнения программы в любой момент времени, когда вам это удобно. Вы можете сделать ре-композицию, когда вы хотите сделать. Вы также можете загружать возможности по требованию (Lazy load) или добавлять метаданные к экспорту. Поиск зависимостей в папке может быть сделано с фильтрацией. Также есть много других возможностей не присущих DI контейнерам.


Managed Extensibility Framework очень интересный и нужный функционал, что был добавлен в NET 4.0 Framework.


Sources: Download MEF.DEMO.ZIP — 118.46 KB
Tags:
Hubs:
+43
Comments 31
Comments Comments 31

Articles