Подтвердить что ты не робот

Каковы наилучшие методы MEF?

Каковы некоторые рекомендации по использованию MEF в вашем коде? Есть ли какие-либо подводные камни, которые следует учитывать при запуске вашего расширяемого приложения? Вы столкнулись с чем-то, о чем раньше знали?

4b9b3361

Ответ 1

Я нахожусь в середине создания полноценного расширяемого приложения на MEF (и используя WPF с шаблоном MVVM). Я взял базовую структуру приложения, которую я построил, и открыл ее как SoapBox Core. Я также опубликовал демо-версию на основе SoapBox Core в Code Project: Создание расширяемого приложения с помощью MEF, WPF и 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

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

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 (которое предварительно запрограммировано для закрытия приложения). Если вы хотите добавить расширение в меню "Файл", вы, вероятно, захотите его отобразить перед пунктом меню "Выход". IMenuItem наследует от IExtension, который имеет два свойства:

  • InsertRelativeToID
  • BeforeOrAfter

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

Ответ 2

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

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

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

Ответ 3

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

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

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

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

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

UPDATE 2010-06-09

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

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

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

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