MEFedMVVM – integracja z Unity

Promuj

Ostatnio interesowała mnie sprawa użycia Unity razem z biblioteka MEFedMVV. Czemu w ogóle rozważałem taka kwestię? Otóż MEF pozwala jedynie zdefiniować na dwa sposoby jak dany obiekt ma być tworzony – czy będzie to jeden obiekt, czy obiekt będzie tworzony za każdym razem. Jeśli chcielibyśmy stworzyć jakieś bardziej zaawansowane reguły tworzenia obiektów (np. tworzenie nowego obiektu tylko dla danego wątku), wtedy musimy posłużyć się już jakimś kontenerem IoC/DI.

Na szczęście ktoś już o tym pomyślał i została stworzona biblioteka MefContrib. Pozwala ona m.in. na integrację Unity z MEF’em. Integracja może przebiegać na trzy różne sposoby:

  • MEF "widzi" typy zdefiniowane w Unity, ale Unity nie "widzi" typów zdefiniowanych w MEF’ie,
  • Unity "widzi" typy zdefiniowane w MEF’ie, ale MEF nie "widzi" typów zdefiniowanych w Unity,
  • MEF i Unity "widzą" wzajemnie zdefiniowane typy.

Omówię tutaj przypadek ostatni. W celu dokonania takiej integracji należy wykonać jedynie 3 linijki kodu:

UnityContainer unityContainer = new UnityContainer();
AssemblyCatalog assemblyCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());

unityContainer.RegisterCatalog(assemblyCatalog);

I to właściwie wszystko co trzeba zrobić, żeby zintegrować MEF’a z Unity. Problemem pozostaje teraz jak to połączyć z biblioteką MEFedMVVM. Niestety MEFContrib oraz MEFedMVVM tworzą instancję klasy CompositionContainer wewnętrznie i nie ma możliwości aby przekazać taką instancję z zewnątrz. Jedyną opcję na konfigurację daję nam MEFedMVVM i jego klasa LocatorBootstrapper. Trzeba tutaj do metody ApplyComposer przekazać instancję klasy, która implementuje interfejs IComposer. Całość kodu przedstawia się następująco:

public class SampleComposer : IComposer
{
    private ComposablePartCatalog catalog;
    private IEnumerable<ExportProvider> exports;

    public SampleComposer(ComposablePartCatalog catalog, IEnumerable<ExportProvider> exports)
    {
        this.catalog = catalog;
        this.exports = exports;
    }

    public IEnumerable<ExportProvider> GetCustomExportProviders()
    {
        return this.exports;
    }

    public ComposablePartCatalog InitializeContainer()
    {
        return this.catalog;
    }
}

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        UnityContainer unityContainer = new UnityContainer();
        AssemblyCatalog assemblyCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());

        unityContainer.RegisterCatalog(assemblyCatalog);
        unityContainer.RegisterType<IHelloWorldService, HelloWorldService>();

        CompositionContainer container = unityContainer.Resolve<CompositionContainer>();
        
        SampleComposer sampleComposer = new SampleComposer(assemblyCatalog, new List<ExportProvider>() { container });
        LocatorBootstrapper.ApplyComposer(sampleComposer);
    }
}

Stworzyłem klasę SampleComposer, która zajmuje się przekazaniem zdefiniowanych typów w Unity do biblioteki MEFedMVVM. Zdefiniowana została też prosta klasa HelloWorldService, która wygląda następująco:

public class HelloWorldService : IHelloWorldService
{
    public HelloWorldService()
    {

    }

    public string GetMessage()
    {
        return "Hello world at: " + DateTime.Now.ToString();
    }
}

Została ona użyta w klasie MainViewModel:

[ExportViewModel("MainViewModel")]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class MainViewModel : INotifyPropertyChanged
{
    private IHelloWorldService helloWorldService;
    private SimpleCommand<object, object> helloWorldCommand;

    public event PropertyChangedEventHandler PropertyChanged;

    public SimpleCommand<object, object> HelloWorldCommand
    {
        get { return this.helloWorldCommand; }
    }

    public string Message
    {
        get;
        set;
    }

    [ImportingConstructor]
    public MainViewModel(IHelloWorldService helloWorldService)
    {
        this.helloWorldService = helloWorldService;
        this.helloWorldCommand = new SimpleCommand<object, object>(this.ShowHelloWorld);
    }

    private void ShowHelloWorld(object param)
    {
        this.Message = this.helloWorldService.GetMessage();
        this.OnPropertyChanged("Message");
    }

    private void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

Konstruktor tej klasy został oznaczony atrybutem ImportingConstructor, który informuje MEF’a o konieczności "wstrzyknięcia" odpowiednich instancji. Typ HelloWorldService został natomiast zarejestrowany w Unity. Jednak dzięki przedstawionej wcześniej integracji MEF "widzi" ten typ.

Przedstawiony w tym poście kod został stworzony na podstawie projektu z wcześniejszego wpisu. Tutaj znajduje się jego rozbudowana wersja, stworzona na potrzeby tego wpisu.

3 Responses to MEFedMVVM – integracja z Unity

  1. Pingback: dotnetomaniak.pl

  2. guest pisze:

    Taka ogólna uwaga. Wszystko ok ale generalnie mógłbyś w każdym swoim wpisie zacząć od jakiegoś wstępu i wyjaśnienia dlaczego to do szczęścia jest potrzebne i dlaczego to jest tak rozwiązane a nie inaczej. Na przykład to ‚czy będzie to jeden obiekt, czy obiekt będzie tworzony za każdym razem’ rozwiązuje wzorzec projektowy ‚singleton’ ale żeby nie było tak prosto to dowiaduję się tutaj o istnieniu jakiejś biblioteki, kolejnej do kolekcji .net która może coś tam, padają jakieś definicje i nazwy a cel pozostaje mglisty. Pozdrawiam

  3. mendoza32 pisze:

    Dzięki za uwagi. Postaram się następne wpisy napisać jeszcze lepiej :>

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Log Out / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Log Out / Zmień )

Facebook photo

Komentujesz korzystając z konta Facebook. Log Out / Zmień )

Google+ photo

Komentujesz korzystając z konta Google+. Log Out / Zmień )

Connecting to %s