Форум программистов, компьютерный форум, киберфорум
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. Показов 10547. Ответов 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
24.11.2015, 16:23

Не по теме:

insite2012, для таких штук есть блоги

0
 Аватар для Adadjio
61 / 61 / 32
Регистрация: 30.07.2013
Сообщений: 178
24.11.2015, 16:37

Не по теме:


nestquik2, пусть лучше будет как отдельная тема в шапке форума, чем искать ее в блоге.
Я лично(думаю я не один такой) чаще пробегаюсь по темам на форуме вместо того чтобы проверять блог на наличие новых тем.



Спасибо автору - тема очень интересная.
0
 Аватар для Konctantin
970 / 773 / 171
Регистрация: 12.04.2009
Сообщений: 1,700
25.11.2015, 16:27
Думаю, что использование дополнительных коллекций в примере:
C#
1
2
3
//Словарь только для чтения, ключ-строка, значение-метод типа 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; }
это плохо, они только запутывают новичка.

В данном случае надо понимать, что с помощью композиции мы ищем в каталогах сборки и создаем экземпляры классов используя интерфейс (либо же можно использовать базовый класс), загружая их в коллекцию.

Так же в официальном примере, показано как использовать в данном случае атрибут ExportMetadata, который и определяет какая операция используется.

Как по мне так подобные "фильтры" (иными словами трудно это назвать) все же лучше реализовывать в виде атрибутов.

ЗЫ. Мне кажется что вы немного переборщили с примером, как бы развили его немножко не в том направлении.
Сам по себе MEF очень интересная штука, и при ее использовании главное правильно спроектировать систему.

ЗЫЫ. так же можно добавить что в качестве каталога композиции может выступать и само приложение.
То есть мы можем размещать классы с атрибутами импорта в основном проекте.
0
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2
25.11.2015, 17:41  [ТС]
Цитата Сообщение от Konctantin Посмотреть сообщение
это плохо, они только запутывают новичка.
Я не думаю, что тема MEF предназначена для совсем уж "новичков".
Цитата Сообщение от Konctantin Посмотреть сообщение
показано как использовать в данном случае атрибут ExportMetadata
Да, я это видел. Однако офф. пример, если мягко сказать, не блещет наглядностью, на мой взгляд. Тем более консоль там ну вообще не в тему.
Цитата Сообщение от Konctantin Посмотреть сообщение
Мне кажется что вы немного переборщили с примером, как бы развили его немножко не в том направлении.
Так я же и сказал, кто может - дополните, развивайте, буду только рад.
Цитата Сообщение от Konctantin Посмотреть сообщение
так же можно добавить что в качестве каталога композиции может выступать и само приложение.
То есть мы можем размещать классы с атрибутами импорта в основном проекте.
Хм, думаю, это не самый распостраненный сценарий. Ну примерно то же самое, что писать одну программу, но часть ее выносить как службу WCF с использованием именованных каналов, чтобы одно приложение было и хостом, и клиентом. Конечно, все это возможно, но вряд ли часто нужно.
0
 Аватар для skilllab
296 / 236 / 58
Регистрация: 03.02.2011
Сообщений: 2,045
Записей в блоге: 1
25.11.2015, 20:55
insite2012, можно ли используя MEF и основываясь, ну допустим на PANEL, создавать так же и "визуальные" плагины? К примеру, количество кнопок в калькуляторе расширить, не используя ComboBox.
0
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2
25.11.2015, 21:02  [ТС]
Цитата Сообщение от skilllab Посмотреть сообщение
можно ли используя MEF и основываясь, ну допустим на PANEL, создавать так же и "визуальные" плагины?
То есть, как я вас понял, менять "шкурки"?
Я сам не пробовал (поскольку сам начал пару дней как с этой темой разбираться), но точно такая идея у меня уже была.
Думаю, что да, это вполне возможно.
0
 Аватар для skilllab
296 / 236 / 58
Регистрация: 03.02.2011
Сообщений: 2,045
Записей в блоге: 1
25.11.2015, 21:09
Ну, не совсем "шкурки"
некорректно выразился. Применить FlowLayuotPanel. Т.е. добавляя контролы со своими обработчиками мы как бы превращаем калькулятор в ... инженерный))) наращивая тем самым функционал + добавляя этот функционал как Control. При этом "подкидывая" всё новые и новые "кнопки" в папку.
0
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2
25.11.2015, 21:16  [ТС]
Цитата Сообщение от skilllab Посмотреть сообщение
Применить FlowLayuotPanel. Т.е. добавляя контролы со своими обработчиками мы как бы превращаем калькулятор в ... инженерный))) наращивая тем самым функционал + добавляя этот функционал как Control. При этом "подкидывая" всё новые и новые "кнопки" в папку.
Как я сказал выше, я пока не пробовал, сейчас пишу программу как раз с использованием MEF, но там нужен именно функционал (несколько dll, реализующих интерфейс с методами генерации последовательностей), но никто не мешает вам попробовать и выложить результаты. Тема, собственно, для этого и создана, направления разработки у всех разные, так что пробуйте, результатами поделитесь со всеми.
0
 Аватар для skilllab
296 / 236 / 58
Регистрация: 03.02.2011
Сообщений: 2,045
Записей в блоге: 1
25.11.2015, 21:20
Цитата Сообщение от insite2012 Посмотреть сообщение
никто не мешает вам
Я, к моему сожалению, "институтов не кончал" по форумной тематике. Всё сам. Так что мои потуги будут лишь мешать. А вот за вашей темой я бы последил.
У меня был как-то опыт общения с разработчиками на предмет создания длл-ок для длл-ок))) Там всё упиралось в навороченную рефлексию. Я как тогда там мало понимал, так и сейчас.
0
Master of Orion
Эксперт .NET
 Аватар для Psilon
6101 / 4957 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
25.11.2015, 21:33
skilllab, да ничего сложного, делаем интерфейс IButton со свойством Operate или что-нибудь в таком духе. После этого плагин находит все IButton и пихает их все в этот лейаут. Если уж совсем кастомизировать, то в IButton добавляется свойство Control, и этот контрол добавляется хоть в FlowLayoutPanel, Хоть куда.
0
 Аватар для sau
2773 / 2073 / 386
Регистрация: 22.07.2011
Сообщений: 7,820
25.11.2015, 22:00
Еще есть МАF , чуть более удобен.
А вообще , хз , расплодилось столько всяких вариаций; Unity, NInject и т.п (притом в исполнении MS), сейчас под каждый фреймворк делают свою версию- у owin-а одни , у mvc - другой , для веб.форм третий , в веб.апи четвертый , к чему это я - к тому , что за всем этим лесом , MEF/MAF как то смотрится устаревшим и практически не вспоминается.
0
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2
25.11.2015, 22:01  [ТС]
skilllab, посмотрите прикрепленный архив. Там, конечно, все коряво, сделал наспех (после вашего вопроса), но контролы передавать можно, так что пробуйте. Я там создавал GroupBox, пока там только один, и с размерами не очень, но это просто тест (после вашего вопроса решил проверить). Пробуйте, экспериментируйте.
Вложения
Тип файла: rar Test.rar (69.1 Кб, 18 просмотров)
0
 Аватар для skilllab
296 / 236 / 58
Регистрация: 03.02.2011
Сообщений: 2,045
Записей в блоге: 1
25.11.2015, 22:10
Цитата Сообщение от insite2012 Посмотреть сообщение
контролы передавать можно
Тупо заменил все GroupBox на Button:
container.SatisfyImportsOnce(this);
"System.Reflection.ReflectionTypeLoadExc eption"


Поставил AnyCPU, очистил всё. Теперь даже не загружается.
0
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2
25.11.2015, 22:13  [ТС]
Цитата Сообщение от skilllab Посмотреть сообщение
Тупо заменил все GroupBox на Button
Возможно, не везде заменили. Там же и саму dll надо изменить, а потом еще и заново ее перегрузить в каталог, предназначенный для поиска сборок композиции (у меня это корневой каталог самого приложения).
Если GroupBox передается, то и Button должен. 100500.
0
 Аватар для skilllab
296 / 236 / 58
Регистрация: 03.02.2011
Сообщений: 2,045
Записей в блоге: 1
25.11.2015, 22:15
Цитата Сообщение от insite2012 Посмотреть сообщение
саму dll надо изменить
Я и написал - везде. Всё очистил, всё пересобрал. Не пашет.
0
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2
25.11.2015, 23:17  [ТС]
Цитата Сообщение от skilllab Посмотреть сообщение
Всё очистил, всё пересобрал. Не пашет.
skilllab, а так?
Вложения
Тип файла: rar Test.rar (66.5 Кб, 15 просмотров)
1
 Аватар для Konctantin
970 / 773 / 171
Регистрация: 12.04.2009
Сообщений: 1,700
26.11.2015, 09:05
C#
1
Хм, думаю, это не самый распостраненный сценарий.
А как показывает практика, совсем наоборот, этот метод очень часто используют (в основном для построения меню приложения).
Например данный способ позволяет добавлять новый функционал не особо привязываясь к архитектуре приложения.
0
Эксперт .NET
 Аватар для insite2012
5548 / 4311 / 1218
Регистрация: 12.10.2013
Сообщений: 12,371
Записей в блоге: 2
26.11.2015, 09:15  [ТС]
Цитата Сообщение от Konctantin Посмотреть сообщение
как показывает практика, совсем наоборот, этот метод очень часто используют
Вполне возможно, не спорю. Был бы очень благодарен, если бы вы выложили свои наработки по данной теме, возможно, кому-то из форумчан они будут полезны. Я и сам посмотрю с удовольствием.
0
Эксперт .NET
 Аватар для Rius
13027 / 7592 / 1662
Регистрация: 25.05.2015
Сообщений: 23,096
Записей в блоге: 14
26.11.2015, 09:22
Наработка
Вы что-то тут сильно сложное намудрили...
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
26.11.2015, 09:22
Помогаю со студенческими работами здесь

Реализация 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...


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

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Как объединить две одинаковые БД Access с разными данными
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru