Do platformy .NET 4 dołączona została rozwijana dotychczas w ramach projektu społecznościowego mef.codeplex.com technologia MEF (od ang. Managed Extensibility Framework – zarządzana platforma rozszerzeń), która ułatwia tworzenie aplikacji korzystających z wtyczek (ang. plug-in) – bibliotek DLL rozszerzających możliwości aplikacji. Celem tego artykułu, przeznaczonego raczej dla studentów i planowanego w trzech częściach, jest łagodne wprowadzenie do tej technologii.
Managed Extensibility Framework (MEF) - część 1: Gospodarz i wtyczka w jednej aplikacji
Do platformy .NET 4 dołączona została rozwijana dotychczas w ramach projektu społecznościowego mef.codeplex.com technologia MEF (od ang. Managed Extensibility Framework - zarządzana platforma rozszerzeń), która ułatwia tworzenie aplikacji korzystających z wtyczek (ang. plug-in) - bibliotek DLL rozszerzających możliwości aplikacji.
Korzystanie z takiej architektury aplikacji jest korzystne z kilku względów. Po pierwsze przeniesienie części funkcjonalności do osobnego projektu wtyczki upraszcza zasadniczy projekt i ułatwia dodawanie do niego kolejnych elementów. Ponadto wtyczka, która jest w końcu rodzajem enkapsulacji kodu ułatwia jego testowanie. Prostsze jest też jej ewentualne aktualizowanie.
W poniższym przykładzie opisany przykład użycia MEF, który jest w pewnym stopniu wzorowany na przykładzie z dokumentacji MEF (zob. http://mef.codeplex.com). Podobnie, jak w umieszczonym na stronie MEF przewodniku dla programistów rozpoczniemy od przykładu, w którym zarówno aplikacja, jak i wtyczka skompilowane są do tego samego pliku .exe. Niebawem przeniesiemy jednak wtyczkę do osobnej biblioteki DLL.
Zacznijmy zatem od aplikacji zawierającej jednocześnie klasę gospodarza MEF (ang. host), jak i importowaną klasę, którą dla ułatwienia, ale niezbyt poprawnie, będę nazywał po prostu wtyczką.
1. Tworzymy projekt aplikacji typu Windows Forms Application.
2. Po jego utworzeniu naciskamy F7, aby przejść do edytora kodu pliku Form1.cs.
3. Korzystając z menu Project, Add Reference dodajemy do projektu bibliotekę System.ComponentModel.Composition.dll.
4. W pliku Form1.cs do sekcji poleceń using dodajemy przestrzenie nazw System.ComponentModel.Composition oraz System.ComponentModel.Composition.Hosting.
5. W pliku Form1.cs, w przestrzeni nazw aplikacji, ale poza klasą Form1 definiujemy klasę, która będzie pełniła rolę właściwego gospodarza MEF. W niej umieszczamy prywatną metodę Compose (zachowałem częściowo angielskie nazwy, aby ułatwić Czytelnikowi porównania z oryginalnym przykładem z dokumentacji MEF), w której tworzony jest pojemnik wtyczek oraz publiczną metodę Run, która na razie ogranicza się tylko do wywołania metody Compose (listing 1).
Listing 1. Definiowanie klasy gospodarza
[Kod] class MefGospodarz { private void Compose() { CompositionContainer pojemnik = new CompositionContainer(); pojemnik.ComposeParts(this); //dodanie instancji klasy gospodarza-wtyczki } public void Run() { Compose(); } }
|
6. Następnie definiujemy jeszcze jedną klasę w przestrzeni nazw aplikacji. Tym razem będzie to klasa reprezentująca wtyczkę. W tym przykładzie będzie to wtyczka wyświetlająca komunikat na ekranie. Dla ułatwienia zdefiniujemy również interfejs IMessageSender, który definiuje metodę Send, której argument to oczywiście treść komunikatu, a wartość informuje o powodzeniu przesłania komunikatu. Zarówno interfejs, jak i implementująca go klasa MessageBoxSender widoczna jest na listingu 2. Klasa ta opatrzona jest atrybutem System.ComponentModel.Composition.Export, który sygnalizuje, że mamy do czynienia z elementem dołączanym (ang. composable part), czyli prototypem wtyczki.
Listing 2. Interfejs i dołączana część (wtyczka)
[Kod] public interface IMessageSender { bool Send(string message); } [Export(typeof(IMessageSender))] public class MessageBoxSender : IMessageSender { public bool Send(string message) { MessageBox.Show(message, "Komunikat"); return true; } } |
7. W klasie MefGospodarz definiujemy własność udostępniającą zaimportowaną wtyczkę, przed którą stawiamy atrybut System.ComponentModel.Composition.Import (listing 3).
Listing 3. Reprezentant dołączanego elementu w klasie gospodarza
[Kod] class MefGospodarz { [Import] public IMessageSender MessageSender { get; set; } ... } |
8. Nowy element (ang. part) możemy teraz jawnie dodać do pojemnika tworzonego w metodzie MefGospodarz.Compose (listing 4). Nie ma to oczywiście nic wspólnego z automatycznym wykrywaniem wtyczek, co jest przecież zasadniczym zadaniem MEF, ale i do tego dojdziemy.
Listing 4. Tworzenie pojemnika wtyczek
[Kod] private void Compose() { CompositionContainer pojemnik = new CompositionContainer(); pojemnik.ComposeParts(this, new MessageBoxSender()); } |
9. Dysponując referencją do zaimportowanej wtyczki możemy w metodzie MefGospodarz.Run umieścić wywołanie metody Send. Pokazuje to listing 5. Ponieważ obiekt, na rzecz którego wywołujemy metodę jest tworzony przez MEF, warto sprawdzić, czy rzeczywiście powstał i zabezpieczyć się na ewentualne błędy.
Listing 5. Metoda Run - miejsce właściwego użycia wtyczek
[Kod] public void Run() { Compose(); if (MessageSender != null) { if (!MessageSender.Send("Treść wiadomości")) MessageBox.Show("Nie udało się dostarczyć wiadomości"); } else MessageBox.Show("Brak wtyczki pozwalającej na dostarczenie wiadomości"); } |
10. Wreszcie przejdźmy do zakładki projektowania interfejsu formy, umieśćmy na niej przycisk, w którego metodzie zdarzeniowej stwórzmy instancję klasy MefGospodarz i uruchommy jej metodę Run. Powiązanie metod Run i Compose jest oczywiście pewnym uproszczeniem. Zwróćmy jednak uwagę, że dzięki lokalnemu tworzeniu instancji klasy-gospodarza MEF, każde wywołanie jej metody Run związane jest z detekcją wtyczek, które mogą w tej sytuacji zmieniać się także w trakcie działania aplikacji. A raczej będą mogły, bo na razie mamy tylko jedną wtyczkę, która, co gorsze, jest na razie „wkompilowana" w główny projekt.
Listing 6. Stworzenie obiektu gospodarza MEF i wywołanie jego metody Run
|
[Kod] private void button1_Click(object sender, EventArgs e) { new MefGospodarz().Run(); }
|
Po skompilowaniu i uruchomieniu aplikacji możemy sprawdzić, że naciśnięcie przycisku powoduje wyświetlenie okna komunikatu. Wiemy, że związek między klasą gospodarza MefGospodarz, a klasą wtyczki MessageBoxSender nie jest bezpośredni - odpowiada za nią technologia MEF. Jednak na razie obie po skompilowaniu znajdują się w tym samym pliku .exe. Z punktu widzenia celu technologii MEF tj. tworzenia bibliotek DLL zawierających wtyczki - przykład ten jest wobec tego zupełnie niepraktyczny. Jak to zmienić pokażę w drugiej części artykułu.
Autor: Jacek Matulewski
Do platformy .NET 4 dołączona została rozwijana dotychczas w ramach projektu społecznościowego mef.codeplex.com technologia MEF (od ang. Managed Extensibility Framework – zarządzana platforma rozszerzeń), która ułatwia tworzenie aplikacji korzystających z wtyczek (ang. plug-in) – bibliotek DLL rozszerzających możliwości aplikacji. Celem tego artykułu (planowanego w trzech częściach) jest przybliżenie Czytelnikom tej technologii.