Что такое лучшие практики MEF? [закрытый]

23
задан marc_s 20 May 2010 в 14:11
поделиться

3 ответа

Я нахожусь в процессе создания полноценного расширяемого приложения на MEF (и использую WPF с паттерном MVVM). Я взял базовую основу приложения, которую я построил, и выложил ее в открытый доступ как SoapBox Core. Я также опубликовал демо-версию, основанную на SoapBox Core, на Code Project: Building an Extensible Application with MEF, WPF, and MVVM.

Я не уверен, что использование MVVM применимо к вам, но если это так, то вы можете многому научиться, посмотрев на реализацию MVVM в MEF. Особенно то, как она импортирует Views.

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

Чтобы все было понятно, я поместил иерархию расширений в набор статических классов. Например, вот все точки расширения, которые предоставляет основной фреймворк:

namespace SoapBox.Core.ExtensionPoints
{
    public static class Host
    {
        public const string Styles = "ExtensionPoints.Host.Styles";
        public const string Views = "ExtensionPoints.Host.Views";
        public const string StartupCommands = "ExtensionPoints.Host.StartupCommands";
        public const string ShutdownCommands = "ExtensionPoints.Host.ShutdownCommands";
    }
    public static class Workbench
    {
        public const string ToolBars = "ExtensionPoints.Workbench.ToolBars";
        public const string StatusBar = "ExtensionPoints.Workbench.StatusBar";
        public const string Pads = "ExtensionPoints.Workbench.Pads";
        public const string Documents = "ExtensionPoints.Workbench.Documents";

        public static class MainMenu
        {
            public const string Self = "ExtensionPoints.Workbench.MainMenu";
            public const string FileMenu = "ExtensionPoints.Workbench.MainMenu.FileMenu";
            public const string EditMenu = "ExtensionPoints.Workbench.MainMenu.EditMenu";
            public const string ViewMenu = "ExtensionPoints.Workbench.MainMenu.ViewMenu";
            public const string ToolsMenu = "ExtensionPoints.Workbench.MainMenu.ToolsMenu";
            public const string WindowMenu = "ExtensionPoints.Workbench.MainMenu.WindowMenu";
            public const string HelpMenu = "ExtensionPoints.Workbench.MainMenu.HelpMenu";
        }
    }

    public static class Options
    {
        public static class OptionsDialog
        {
            public const string OptionsItems = "ExtensionPoints.Options.OptionsDialog.OptionsItems";
        }
    }
}

Итак, если вы хотите, чтобы ваше расширение добавило что-то в меню файлов, вы экспортируете что-то, реализующее IMenuItem с контрактным именем SoapBox.Core.ExtensionPoints.Workbench.MainMenu.FileMenu

Каждое расширение имеет "ID", который является просто строковым идентификатором. Эти существующие идентификаторы определены в другой иерархии:

namespace SoapBox.Core.Extensions
{
    public static class Workbench
    {
        public static class MainMenu
        {
            public const string File = "File";
            public const string Edit = "Edit";
            public const string View = "View";
            public const string Tools = "Tools";
            public const string Window = "Window";
            public const string Help = "Help";

            public static class FileMenu
            {
                public const string Exit = "Exit";
            }

            public static class ViewMenu
            {
                public const string ToolBars = "ToolBars";
            }

            public static class ToolsMenu
            {
                public const string Options = "Options";
            }
        }
    }
}

Как вы видите, FileMenu уже содержит расширение Exit (которое запрограммировано на закрытие приложения). Если бы вы хотели добавить расширение в меню File, вы, вероятно, хотели бы, чтобы оно появилось перед пунктом меню Exit. IMenuItem наследуется от IExtension, который имеет два свойства:

  • InsertRelativeToID
  • BeforeOrAfter

Таким образом, ваше расширение будет возвращать SoapBox.Core.Extensions.Workbench.MainMenu.FileMenu.Exit для InsertRelativeToID, и возвращать Before для свойства BeforeOrAfter (перечисление). Когда верстак импортирует все расширения меню File, он сортирует все на основе этих идентификаторов. Таким образом, более поздние расширения вставляются относительно существующих расширений.

7
ответ дан 29 November 2019 в 03:06
поделиться

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

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

Также посмотрите здесь http://blogs.microsoft.co.il/blogs/bnaya/archive/2010/01/09/mef-for-beginner-toc.aspx И, конечно же, вы знаете, что можете найти информацию здесь: http://mef.codeplex.com

6
ответ дан 29 November 2019 в 03:06
поделиться

Я все еще новичок в MEF, но я хотел добавить больше к этому обсуждению, потому что я постоянно прохожу через ад, пытаясь понять, почему что-то не работает Я ожидаю, что они это сделают.

Прежде всего, когда вы работаете с MEF, я рекомендую добавить System.ComponentModel.Composition в ваше решение, а не просто добавлять ссылки на сборки. Хотя проблемы с отладкой в ​​MEF кажутся рекурсивным кошмаром, они абсолютно неизбежны и жизненно важны, когда вы не можете понять, что идет не так.

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

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

Я бы хотел, чтобы кто-нибудь отправил ответ на этот вопрос об архитектурах, которые облегчают интеграцию MEF. Ответ относительно меню панели инструментов и тому подобного действительно хорош, но я хотел бы увидеть что-то, что говорит о вещах, которые полностью находятся на стороне модели MVVM.Например, как должны быть организованы менеджеры плагинов, базы данных, плагины и разделяемые библиотеки. Я все еще пытаюсь понять, почему у меня было достаточно хорошее время, чтобы заставить работать мое первое приложение MEF, но после того, как я получил больше «опыта» с ним, я не могу заставить мое новое приложение работать на 100%.

ОБНОВЛЕНИЕ 2010-06-09

Я хотел бы добавить еще одну возможную практику, которая поможет вам ориентироваться в кустах MEF. Сегодня мне нужно было проверить работоспособность этого проекта, который я «просто не мог понять», поэтому я создал простую нетрадиционную диаграмму классов UML, в которой я использовал зависимости для отметки импорта и экспорта. Вот то, что я обнаружил, и это очень прояснило проблему.

Рабочее приложение: alt text

Нерабочая заявка: alt text

Разве это не глупо? Модель не была загружена и находилась на острове сама по себе. Я считаю, именно поэтому мои зависимости на основе MEF не решаются (если кто-нибудь может исправить меня, если я ошибаюсь, я был бы признателен!)

2
ответ дан 29 November 2019 в 03:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: