Форум программистов, компьютерный форум, киберфорум
C# .NET
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.83/48: Рейтинг темы: голосов - 48, средняя оценка - 4.83
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2

MEF и создание расширяемого приложения

24.11.2015, 15:28. Показов 10815. Ответов 39
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Итак, на создание этой темы меня толкнуло желание поделиться тем немногим, что мне удалось выяснить по данной теме. Сразу скажу - я не профессионал, а в данной теме так вообще новичок(буквально вчера начал разбираться), так что если у кого-то будут какие замечания, дополнения (именно по затронутому вопросу) - буду рад выслушать.
Не секрет, что создание расширяемых и в то же время простых приложений - не простая задача. Приходится работать с доменами приложений, сборками, использовать отражение, конфигурационные файлы и прочее. И если для людей более-менее сведущих это еще по силам, то для обычных пользователей программ это задача сложна.
С другой стороны, и нам, тем, кто разрабатывает подобные приложения, тоже хотелось бы избавить себя от всех проблем, связанных с созданием расширяемых приложений. Именно для таких целей и была разработана платформа MEF - Managed Extensibility Framework. Она избавляет разработчика от многих проблем и упрощает его работу.
В качестве примера я покажу код классической программы - калькулятора. Однако, в отличие от тех примеров, что мне удалось найти в сети (информация по MEF довольно скупа, несколько статей, и нет ясности, выражающей суть создания приложения с плагинами), мой пример сделан именно в форме. Сделано это специально, для большей наглядности работы. И если в других случаях консоль обеспечивает лучшую наглядность, то тут, на мой взгляд, все наоборот.
Структурно программа состоит из основной части - формы с обработчиками событий интерфейса, класса менеджера плагинов, инкапсулирующего в себе всю логику загрузки плагинов и предоставления имеющейся в них функциональности, и собственно плагинов - библиотек dll, реализующих общедоступный интерфейс основного приложения.
Сам код менеджера плагинов прост, буквально несколько строк, однако этого вполне хватает. Достаточно создать проект библиотеки, добавить две ссылки - на основное приложение и на сборку System.ComponentModel.Composition,
и в классе библиотеки реализовать общедоступный интерфейс, снабдив класс атрибутом экспорта. После этого все, что надо для подключения плагина - просто поместить готовую библиотеку в каталог приложения (или в его подкаталог, как указано в комментариях в коде), и плагин будет подключен при загрузке приложения. Аналогично, для его удаления или предоставления нового достаточно удалить/заменить файл плагина, все остальное среда выполнения сделает за нас.
Весь код программы снабжен комментариями, возможно, терминология не везде точна, но это моя первая программа с использованием MEF. Ниже код, и собственно сам проект в архиве.
Код основной формы:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
 
namespace MEFApplication {
    public partial class MainForm : Form {
        //Переменная класса менеджера плагинов
        PluginManager manager = null;
        public MainForm() {
            InitializeComponent();
            //Подключение обработчика загрузки приложения
            this.Load += new EventHandler(MainForm_Load);
            //Подключение обработчиков нажатия кнопок
            btnCalculate.Click += new EventHandler(btnCalculate_Click);
            btnClear.Click += new EventHandler(btnClear_Click);
        }
        //Обработчик кнопки <Очистить!>
        void btnClear_Click(object sender, EventArgs e) {
            //Очистка всех текстовых полей
            this.Controls.OfType<TextBox>().ToList().ForEach(t => t.Clear());
        }
        //Обработчик кнопки <Вычислить!>
        void btnCalculate_Click(object sender, EventArgs e) {
            //Проверка переменной менеджера плагинов на null
            if (manager == null) {
                return;
            }
            string ops = cmbOperations.Text;
            //Проверка входных аргументов
            if (!string.IsNullOrWhiteSpace(ops)) {
                int arg1;
                int arg2;
                if (!int.TryParse(txtFirstNumber.Text, out arg1)) {
                    txtFirstNumber.Clear();
                    MessageBox.Show("Не верный ввод!", "Ошибка");
                    return;
                }
                if (!int.TryParse(txtSecondNumber.Text, out arg2)) {
                    txtSecondNumber.Clear();
                    MessageBox.Show("Не верный ввод!", "Ошибка");
                    return;
                }
                //Вычисление и вывод результата
                int result = manager._operations[ops](arg1, arg2);
                txtResult.Text = result.ToString();
            }
        }
        //Обработчик загрузки приложения
        void MainForm_Load(object sender, EventArgs e) {
            manager = new PluginManager();
            if (manager.Operations!=null && manager.Operations.Length != 0) {
                cmbOperations.Items.AddRange(manager.Operations);
                cmbOperations.Text = cmbOperations.Items[0].ToString();
            }
        }
    }
}
Код класса-менеджера плагинов:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
using System.IO;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
 
namespace MEFApplication {
    public class PluginManager {
        //Обобщенная коллекция с параметром типа IOperation
        //После композиции содержит все найденные типы, 
        //реализующие IOperation 
        //и помеченные атрибутом экспорта [Export(typeof(IOperation))]
        //В данном случае применен атрибут ImportManyAttribute,
        //для того чтобы все найденные части были добавлены в композицию
        [ImportMany(typeof(IOperation))]
        IEnumerable<IOperation> Plugins { get; set; }
 
        //Словарь только для чтения, ключ-строка, значение-метод типа Func<int, int, int>
        public readonly Dictionary<string, Func<int, int, int>> _operations = 
            new Dictionary<string, Func<int, int, int>>();
        public string[] Operations { get; private set; }
 
        //Конструктор экземпляра
        public PluginManager() {
            //Основной каталог композиции объектов
            AggregateCatalog catalog = new AggregateCatalog();
            //Добавление подкалогов в основной каталог композиции
            //В данном случае добавляется два подкаталога, которые указывают, 
            //в каком месте могут содержаться типы расширения
            //Путь №1 - основная папка приложения (там же, где находится исполняемый файл)
            //Путь №2 - папка Plugins, находящаяся рядом с исполняемым файлом
            //Путей может быть и больше!
            catalog.Catalogs.Add(new DirectoryCatalog(AppDomain.CurrentDomain.BaseDirectory));
            catalog.Catalogs.Add(new DirectoryCatalog(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins")));
            //Контейнер композиции
            CompositionContainer container = new CompositionContainer(catalog);
            //Выполнение композиции найденных частей
            container.ComposeParts(this);
            //Заполнение словаря символами операций и операциями
            if (Plugins.Count() != 0) {
                Plugins.ToList().ForEach(p => _operations.Add(p.Operation, (i, j) => p.Operate(i, j)));
                Operations = _operations.Keys.ToArray();
            }
        }
    }
    //Общедоступный интерфейс   
    public interface IOperation {
        //Свойство для чтения, знак операции
        string Operation { get; }
        //Метод, выполняет операцию над двумя типами int и возвращает тип int
        int Operate(int x, int y);
    }
}
Код одного из плагинов (остальные написаны аналогично):
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
using MEFApplication;
using System.ComponentModel.Composition;
 
namespace MultPlugin {
    //Атрибут экспорта, обявляет данный тип экспортирует IOperation
    [Export(typeof(IOperation))]
    //Класс, реализующий операцию умножения
    public class MultClass : IOperation {
        public int Operate(int x, int y) {
            return x * y;
        }
 
        public string Operation {
            get { return "*"; }
        }
    }
}
Ну и файлы, вид основной формы и архив. Всем спасибо за внимание.
Миниатюры
MEF и создание расширяемого приложения  
Вложения
Тип файла: rar MEFApplication.rar (125.6 Кб, 101 просмотров)
13
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
24.11.2015, 15:28
Ответы с готовыми решениями:

MEF создание элементов в разных потоках
Может кто-нибудь разьяснить в чем подвох? Вьідает ошибку на єтапе container.ComposeParts(service); плана Выполняется составление другого...

MEF создание единого репозитория для запроса
В продолжение єтой темы... https://www.cyberforum.ru/csharp-net/thread1243116.html Каждый контроллер имеет свой набор сервисов,...

Mef и контракты
начал разбираться с mef, в доках написано что контракты должны совпадать, то есть public class MyClass { - можно typeof не...

39
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2
26.11.2015, 09:34  [ТС]
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от Rius Посмотреть сообщение
Вы что-то тут сильно сложное намудрили...
Еще бы пример использования данного проекта не помешал.
Это же dll, как я понимаю...
И как такового использования MEF там нет, рефлексия, так ведь? Судя по этим строкам:
C#
1
2
3
4
5
6
7
8
var plugins = from filename in files.AsParallel()
                              let assembly = Assembly.LoadFile(filename)
                              from attribute in assembly.GetCustomAttributes(typeof(AssemblyPluginTypeAttribute), false).AsParallel()
                              let pluginType = (attribute as AssemblyPluginTypeAttribute).PluginType
                              let interfaceType = pluginType.GetInterface(typeof(IPlugin).FullName)
                              where interfaceType != null && !pluginType.IsAbstract
                              let plugin = (IPlugin)Activator.CreateInstance(pluginType)
                              select plugin;
Динамическое создание объектов через рефлексию.
0
Эксперт .NET
 Аватар для Rius
13307 / 7764 / 1685
Регистрация: 25.05.2015
Сообщений: 23,651
Записей в блоге: 14
26.11.2015, 09:44
Это солюшн с exe и кучей dll. Простое приложение.
Примером является само это приложение и его исходники. (И подопытным кроликом тоже.)
Здесь создаётся сборщик плагинов.

Все кнопки собираются из плагинов.
1
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2
26.11.2015, 09:55  [ТС]
Цитата Сообщение от Rius Посмотреть сообщение
Это солюшн с exe и кучей dll.
Что-то у меня ни одна из Студий (2010, 2015) не желают собирать его в Windows-приложение. Наверное, мне не понять его логики.
0
Эксперт .NET
 Аватар для Rius
13307 / 7764 / 1685
Регистрация: 25.05.2015
Сообщений: 23,651
Записей в блоге: 14
26.11.2015, 09:56
insite2012, да, рефлексия. Из обсуждения выше плюшек от MEF не разглядел.
Почитал Managed Extensibility Framework (MEF) - MSDN, попробую прикрутить.

Что-то у меня ни одна из Студий (2010, 2015) не желают собирать его
зависимости возможно требуются и git. Не готовое оно ещё для сборки везде и всюду.
0
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2
26.11.2015, 10:00  [ТС]
Цитата Сообщение от Rius Посмотреть сообщение
Из обсуждения выше плюшек от MEF не разглядел.
Ну вот тут как раз тема MEF и обсуждается, позволяющая избавиться от всех этих сложностей с AppDomain, Assembly и всего подобного. Поменял dll в папке с плагинами (или добавил еще одну)-и функционал изменился, не трогая основную программу.
0
Эксперт .NET
 Аватар для Rius
13307 / 7764 / 1685
Регистрация: 25.05.2015
Сообщений: 23,651
Записей в блоге: 14
26.11.2015, 10:06
Не, внешне Вы описываете тот же эффект:
Поменял dll в папке с плагинами (или добавил еще одну)-и функционал изменился, не трогая основную программу.
.
То, что там описано в "Проблема расширяемости", сложностей не представляет. Вот "Возможности" интереснее.
0
Master of Orion
Эксперт .NET
 Аватар для Psilon
6102 / 4958 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
26.11.2015, 11:52
Rius, рефлексия вместо MEF - это троллейбус из буханки Конечно можно... Но зачем?
0
 Аватар для Konctantin
970 / 773 / 171
Регистрация: 12.04.2009
Сообщений: 1,700
27.11.2015, 08:57
Не знаю кому как, но мне в свое время сильно упростило жизнь вызов конструктора с параметрами.
(Я не стану расписывать как пользоваться контрактами для вызова нужного конструктора, если их несколько, все это при необходимости можно найти в документации)
Вот пример.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
interface IPlugin {
    string TestMethod();
}
 
[Export(typeof(IPlugin))]
public class Test : IPlugin
{
    string lol;
 
    [ImportingConstructor]
    public Test(string str)
    {
        lol = str;
    }
 
    public string TestMethod()
    {
        return lol;
    }
}
 
class Program
{
    [ImportMany(typeof(IPlugin))]
    IEnumerable<IPlugin> Plugins { get; set; }
 
    public Program()
    {
        var catalog = new AggregateCatalog();
        catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
        //catalog.Catalogs.Add(new DirectoryCatalog(AppDomain.CurrentDomain.BaseDirectory));
 
        var container = new CompositionContainer(catalog);
        container.ComposeExportedValue("hello");
        container.ComposeParts(this);
    }
 
    static void Main(string[] args)
    {
        var p = new Program();
        foreach (var plugin in p.Plugins)
            Console.WriteLine(plugin.TestMethod());
        Console.ReadLine();
    }
}
2
Эксперт .NET
 Аватар для Rius
13307 / 7764 / 1685
Регистрация: 25.05.2015
Сообщений: 23,651
Записей в блоге: 14
15.01.2016, 06:37
Попробовал нконец MEF прикрутить.
Обнаружилось, что время загрузки плагинов увеличилось с 350мс до 1200мс. Чяднт?
0
Master of Orion
Эксперт .NET
 Аватар для Psilon
6102 / 4958 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
15.01.2016, 08:13
Rius, ну так alt + F2 и узнаем, где тупит, логично же
0
Эксперт .NET
 Аватар для Rius
13307 / 7764 / 1685
Регистрация: 25.05.2015
Сообщений: 23,651
Записей в блоге: 14
15.01.2016, 09:40
Psilon, если это о запуске отладки, то у меня F5.
Оно не тупит, просто композиция выполняется в 3-4 раза дольше, чем то же вручную.
Там коду-то - создание каталога, да выполнение композиции.
И из этих 1200мс на
C#
1
this.mContainer.ComposeParts(this);
уходит 900 мс.
0
Master of Orion
Эксперт .NET
 Аватар для Psilon
6102 / 4958 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
15.01.2016, 09:49
Rius, у тебя в папке много dll? Возможно он просто долго сканирует папку.
0
Эксперт .NET
 Аватар для Rius
13307 / 7764 / 1685
Регистрация: 25.05.2015
Сообщений: 23,651
Записей в блоге: 14
15.01.2016, 09:53
Psilon, 7 директорий, 24 dll, из них 10 - плагины.
У меня там 2 функции (по ссылке выше), которые я переключаю. Одна вручную всё собирает, другая через MEF. Всё остальное не меняется.
0
Master of Orion
Эксперт .NET
 Аватар для Psilon
6102 / 4958 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
15.01.2016, 09:59
Rius, к сожалению MEF сам по себе достаточно тормознутая штука, которая все через рефлексию делает... Да, он пытается что-то там кэшировать и все прочее. Но свое узкоспециализированное решение на небольших задачах всегда будет лучше. Поэтому возможно и париться не стоит с его добавлением. Кстати интересно, как ручная сборка работает?

Добавлено через 2 минуты
Ну и возможно стоит посмотреть в сторону юнити
0
Эксперт .NET
 Аватар для Rius
13307 / 7764 / 1685
Регистрация: 25.05.2015
Сообщений: 23,651
Записей в блоге: 14
15.01.2016, 10:03
Psilon, ну интересно попариться, вдруг что-то дельное в этом есть Не зря же тему создали.

Цитата Сообщение от Psilon Посмотреть сообщение
Кстати интересно, как ручная сборка работает?
Ручная сборка и есть тот самый троллейбус из буханки с рефлексией.
Код по ссылке в посте MEF и создание расширяемого приложения
0
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2
15.01.2016, 12:53  [ТС]
Цитата Сообщение от Rius Посмотреть сообщение
композиция выполняется в 3-4 раза дольше, чем то же вручную.
Rius, да, MEF выполняется не быстро, но с учетом вашего решения, с таким количеством плагинов, на мой взгляд это не очень долго. Кстати, я по коду не совсем понял, вы используете отложенную инициализацию или обычную (гитхабом не пользуюсь, поэтому могу только глянуть код, что-то запустить оттуда для меня трудновато )?
0
Эксперт .NET
 Аватар для Rius
13307 / 7764 / 1685
Регистрация: 25.05.2015
Сообщений: 23,651
Записей в блоге: 14
15.01.2016, 12:56
insite2012, чтобы можно было примерно сравнивать, оба способа используют не отложенную инициализацию.

Не по теме:

Плагинов, кстати, не много. В другом проекте их порядка 80-ти.

0
Master of Orion
Эксперт .NET
 Аватар для Psilon
6102 / 4958 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
15.01.2016, 14:35
Rius, гитхаб все серым закрасил, проглядел

Ну так-то да, за гибкость приходится платить производительностью. Не все фреймворки от МС хороши.
0
Эксперт .NET
 Аватар для Rius
13307 / 7764 / 1685
Регистрация: 25.05.2015
Сообщений: 23,651
Записей в блоге: 14
15.01.2016, 18:00
Скорее, гибкость как раз в самопальном решении. А в MEF это плата за "готовое упрощение работы":
Цитата Сообщение от insite2012 Посмотреть сообщение
С другой стороны, и нам, тем, кто разрабатывает подобные приложения, тоже хотелось бы избавить себя от всех проблем, связанных с созданием расширяемых приложений. Именно для таких целей и была разработана платформа MEF - Managed Extensibility Framework. Она избавляет разработчика от многих проблем и упрощает его работу.
0
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2
15.01.2016, 18:35  [ТС]
Цитата Сообщение от Rius Посмотреть сообщение
А в MEF это плата за "готовое упрощение работы":
Естественно. Ближайший, на мой взгляд, аналог - ADО.NET и EF. Первый быстрее, но и возиться с ним дольше. Второй проще, но и тормознут.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
15.01.2016, 18:35
Помогаю со студенческими работами здесь

Реализация Managed Extensibility Framework (MEF)
Вечер добрый, не как не могу понять принцип обмена информацией между приложением и плагином технологией MEF. Точнее: С Подключением и...

MEF. Выборочная загрузка. Фильтрация модулей.
Пишу приложение с поддержкой системы плагинов. Не могу разобраться как сделать правильную фильтрацию модулей. Например, чтобы программа...

Работа с MEF. Инъекция зависимостей через конструктор
Кто-нибудь шарит в MEF? Вообщем у меня проблема следующего характера, хочу написать расширяющие приложение. Расширять хочу с помощью MEF...

MEF from Attribute. Дэбаг показывает, что StatusService == null
Есть контроллер public class ScreenController : BaseController { public ActionResult Action() { /* TODO */ } ...

Привязка к TabControl с использованием MEF
Всем привет, для создания модульного приложения использую MEF. Интерфейс плагинов выглядит следующим образом: public interface...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
[golang] Двоичная куча, min-heap
alhaos 20.05.2026
Двоичная куча Двоичная куча — структура данных, которая всегда держит самый важный элемент наготове. Представьте очередь к хилеру в игре, и очередь из игроков в приоритете те у кого меньше. . .
[golang] Breadth-First Search
alhaos 19.05.2026
BFS (Breadth-First Search) — это базовый алгоритм обхода графа в ширину, который поуровнево исследует все связанные вершины. Он начинает с выбранной точки и проверяет всех соседей, прежде чем. . .
[golang] Алгоритм «Хак Госпера»
alhaos 17.05.2026
Алгоритм «Хак Госпера» Хак Госпера (Gosper's Hack) — алгоритм нахождения следующего по величине числа с тем же количеством установленных бит. Придуман Биллом Госпером в 1970-х, опубликован в. . .
Рисование бинарного древа до 6-го колена на js, svg.
russiannick 17.05.2026
<svg width="335" height="240" viewBox="0 0 335 240" fill="#e5e1bb"> <style> <!]> </ style> <g id="bush"> </ g> </ svg> function fn(){ let rost;/ / высота древа let xx=165,yy=210,w=256;
FSharp: interface of module
DevAlt 16.05.2026
Интерфейс модуля F# позволяет управлять доступностью членов, содержащихся в реализации модуля. По-умолчанию все члены модуля доступны: module Foo let x = 10 let boo () = printfn "boo" . . .
Хитросплетение родственных связей пантеона греческих богов.
russiannick 14.05.2026
Однооконник, позволяющий узреть и изучить отдельных героев древней Греции. <!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible". . .
[golang] Угол между стрелками часов
alhaos 12.05.2026
По заданным значениям часа и минуты необходимо определить значение меньшего угла между стрелками аналогового циферблата часов. import "math" func angleClock(hour int, minutes int) float64 { . . .
Debian 13: Установка Lazarus QT5
ВитГо 09.05.2026
Эта инструкция моя компиляция инструкций volvo https:/ / www. cyberforum. ru/ blogs/ 203668/ 10753. html и его же старой инструкции по установке Lazarus с gtk2. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru