Jakie są najlepsze praktyki MEF? [zamknięte]

Jakie są najlepsze praktyki korzystania z MEF w kodzie? Czy są jakieś pułapki, które należy wziąć pod uwagę przy uruchamianiu rozszerzalnej aplikacji? Wpadłeś na coś, co powinieneś wiedzieć wcześniej?

Author: marc_s, 2010-04-29

3 answers

Jestem w trakcie budowania pełnowartościowej aplikacji rozszerzalnej na MEF (i korzystania z WPF z wzorcem MVVM). Wziąłem podstawowy framework aplikacji, który zbudowałem i udostępniłem go jako Soapbox Core . Opublikowałem również demo oparte na Soapbox Core w Code Project: budowanie rozszerzalnej aplikacji z MEF, WPF i MVVM.

Nie jestem pewien, czy korzystanie z MVVM dotyczy ciebie, ale jeśli tak, to wiele możesz się nauczyć patrząc na implementację MVVM z MEF. Szczególnie sposób, w jaki importuje poglądy.

Jeśli chodzi o najlepsze praktyki... Stworzyłem zagnieżdżoną hierarchię rozszerzeń (więc podstawowy moduł nazywa się Host, a wszystko, co robi, to komponuje aplikację i importuje kilka podstawowych rozszerzeń). Następnie te rozszerzenia ujawniają inne punkty rozszerzeń i aplikacja buduje się sama podczas jej uruchamiania (skrzyżowanie kompozycji i rozszerzeń).

Aby wszystko było jasne, umieściłem hierarchię rozszerzeń w zbiorze klasy statyczne. Na przykład, oto wszystkie punkty rozszerzenia, które zapewnia core framework:

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";
        }
    }
}

Więc jeśli chcesz, aby Twoje rozszerzenie dodało coś do menu Plik, wyeksportowałbyś coś, co implementuje IMenuItem z nazwą kontraktu SoapBox.Rdzeń.ExtensionPoints.Stół warsztatowy.MainMenu.FileMenu

Każde rozszerzenie ma "ID", który jest tylko identyfikatorem łańcucha znaków. Te istniejące identyfikatory są zdefiniowane w innej hierarchii:

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";
            }
        }
    }
}

Jak widać FileMenu już zawiera rozszerzenie Exit (które jest wstępnie zaprogramowane, aby zamknąć aplikację). Jeśli chcesz dodać rozszerzenie do menu Plik, prawdopodobnie chcesz, aby pojawiło się przed zakończeniem menu. IMenuItem dziedziczy z IExtension, który ma dwie właściwości:

  • InsertRelativeToID
  • Beforeoropter

Więc Twoje rozszerzenie zwróci SoapBox.Rdzeń.Rozszerzenia.Stół warsztatowy.MainMenu.FileMenu.Exit for InsertRelativeToID, and would return Before for the Beforeorofter property (an wyliczenia). Gdy workbench importuje wszystkie rozszerzenia Menu plików, sortuje wszystko na podstawie tych identyfikatorów. W ten sposób późniejsze rozszerzenia wstawiają się względem istniejących rozszerzeń.

 7
Author: Scott Whitlock,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-07 02:47:07

Najlepszą praktyką jest użycie modelu współdzielonego (singleton). To prowadzi nas do rozważenia projektowania, które sugerują, że powinieneś zaprojektować swoje eksportowalne części jako bezpaństwowe i bezpieczne dla wątków, aby nie miały wpływu na wiele wywołań (może w różnych wątkach) na tej samej instancji.

W przypadkach, gdy model singleton nie jest dla Ciebie odpowiedni, zaleca się użycie wzorca konstruktora (aby oddzielić wyeksportowaną część od rzeczywistej instancji). Należy pamiętać, że za pomocą model nie współdzielony jest dość kosztowny, ponieważ wykorzystuje odbicie dla rzeczywistej instancji (używając wzoru konstruktora można uzyskać ten sam wynik przy mniejszym bólu).

Zobacz też tutaj http://blogs.microsoft.co.il/blogs/bnaya/archive/2010/01/09/mef-for-beginner-toc.aspx I oczywiście wiesz, że info znajdziesz tutaj: http://mef.codeplex.com

 6
Author: Huds0nHawk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-06 15:07:00

Wciąż jestem nowy w MEF, ale chciałem dodać więcej do tej dyskusji, ponieważ ciągle przechodzę przez piekło, próbując dowiedzieć się, dlaczego rzeczy nie działają tak, jak oczekuję.

Przede wszystkim, gdy pracujesz z MEF, polecam dodanie systemu.ComponentModel.Skład do rozwiązania, a nie tylko dodawanie odniesień do złożeń. Chociaż debugowanie problemów w MEF wydaje się rekurencyjnym koszmarem, jest absolutnie nieuniknione i ważne, gdy nie możesz zrozumieć co jest nie tak.

To prowadzi mnie do następnego punktu, który polega na tym, aby nigdy, przenigdy nie zapominać, że MEF nie wie o tym, czego nie mówisz, lub jeśli nie mówisz tego poprawnie . Na przykład, moja aplikacja Alfa działała świetnie z MEF - miałem komponować części w głównym GUI, a wszystkie zespoły, które zostały załadowane przez kontener (które były zależnościami głównej aplikacji) wyeksportowały niezbędne interfejsy. Wszystko dobrze się ułożyło I udało mi się namówić MEF do rozwiązania przypadki, kiedy i gdzie tego chciałem.

Jednak dopiero zacząłem pracować nad kolejną wersją; niektóre wtyczki załadowane (te, które eksportowały interfejs, ale nie miały wymagań importowych), podczas gdy inne nie (te, które potrzebowały importu). Tym razem komponowałem części w mojej klasie ApplianceManager, która odpowiada za ładowanie wtyczek, ale wtyczki potrzebne do rozwiązania importu z innych klas w aplikacji(w moim przypadku Model). Pomyślałem, że powinno to nastąpić automatycznie., zwłaszcza, że widzę w konstrukcji katalogu, że te zespoły zostały wykryte... ale nadal nie mogę go uruchomić... co sprowadza mnie z powrotem do mojego pierwszego punktu -- dodać kod źródłowy, a nie same zespoły. Debugowanie tego prawie doprowadza mnie do szaleństwa, ale w końcu to rozgryzę po wielu pilnych krokach przez kod MEF. :)

Chciałbym, aby ktoś opublikował odpowiedź na to pytanie, która mówi o architekturach ułatwiających łatwą integrację MEF. Na odpowiedź dotycząca menu paska narzędzi i taka jest naprawdę dobra, ale chciałbym zobaczyć coś, co mówi o rzeczach, które znajdują się całkowicie po stronie Modelu MVVM. Na przykład jak powinny być rozmieszczone Menedżery wtyczek, bazy danych, wtyczki i biblioteki współdzielone. Nadal staram się dowiedzieć, dlaczego miałem dość w porządku czas coraz moja pierwsza aplikacja MEF do pracy, ale po coraz więcej "doświadczenie" z nim, nie mogę dostać moja nowa aplikacja do pracy 100%.

Aktualizacja 2010-06-09

Chciałbym dodaj kolejną możliwą praktykę, która pomoże Ci poruszać się po krzewie MEF. Dzisiaj musiałem sprawdzić stan psychiczny tego projektu, którego "po prostu nie mogłem rozgryźć", więc stworzyłem prosty, niekonwencjonalny diagram klas UML, w którym używałem zależności do oznaczania importu i eksportu. Oto, co znalazłem, co wyjaśniło sprawę bardzo jasno.

Aplikacja robocza:

Niedziałająca aplikacja:

Czy to nie głupie? Model nie był załadowany, a był na wyspie przez siebie. I wierzę dlatego moje zależności oparte na MEF nie są rozwiązane (jeśli ktoś może mnie corret mnie, jeśli się mylę tutaj, byłbym wdzięczny, choć!)

 2
Author: Dave,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-08 14:26:43