Introduzione a MEF

In questo periodo l’universita’ o meglio un suo laboratorio di ricerca mi ha dato l’opportunita’ (oltre che lo stimolo) di studiare MEF e le Parallel Extensions utilizzando il .NET 3.5, ed io ho colto la palla al balzo!

Cosi’ ho deciso di scrivere una serie di post su MEF sperando che possano interessare a qualcuno. Di seguito trovate la lista dei post che seguiranno nelle prossime settimane:

  1. Introduzione a MEF
  2. Imports & Exports
  3. Catalogs
  4. Recomposition
  5. Metadata
  6. Internals? “Fluent Composition”?

Cos’e’ MEF?
Il Managed Extensibility Framework (MEF) permette la creazione di applicazioni estendibili in maniera semplice e veloce evitando al programmatore di crearsi una propria infrastruttura per la gestione dei plugins.

Come funziona?
Dall’immagine sottostante possiamo notare che MEF puo’ essere “diviso” in 3 parti:

  1. I Catalogs (DirectoryCatalog, AssemblyCatalog, TypeCatalog, CustomCatalog) che si occupano di scovare i nostri plugins
  2. Il CompositionContainer che soddisfa le dipendenze ed espone gli Exports al mondo esterno
  3. Le Parts o meglio le Composable Parts che possono offrire dei servizi (Exports) e ne possono importare altri (Imports) da altre Composable Parts.

 

clip_image002

Una Composable Part e’ sostanzialmente l’istanza di una nostra classe che espone o utilizza dei servizi ed ogni Import/Export avra’ un determinato contratto che verra’ specificato tramite degli attributi. Il Container interagisce con i Catalogs per avere accesso alle Composable Parts e quindi risolvere le dipendenze.

Ora che abbiamo un’infarinatura molto generale di cosa sia MEF e di come funzioni diamo un’occhiata alla prima applicazione.

clip_image004

Abbiamo creato due progetti il primo (IntroductionToMEFSample) che contiene un’applicazione di prova, il secondo che contiene il contratto che i plugins dovreanno rispettare.

clip_image006

L'applicazione di prova e’ un progetto WPF dove utilizzo il pattern MVVM ed in particolare la versione implementata da Sacha Barber chiamata Cinch (in realta’ non e’ un semplice framework MVVM…infatti dentro troviamo un bel po’ di marchingegni utili). Ho utilizzato Cinch al posto di altri framework MVVM perche’ non l’avevo mai provato, quindi non uso nessuna chicca particolare.

Come possiamo vedere il MainViewModel e’ molto semplice…espone semplicemente una proprieta’ Plugin che conterra’ il plugin caricato da MEF. La proprieta’ Plugin pero’ e’ decorata con l’attributo Import che dira’ a MEF ed in particolare al CompositionContainer che deve essere valorizzata.

clip_image008

La classe MainViewModel invece e’ decorata con l’attributo Export che dice al CompositionContainer che il MainViewModel e’ l’implementazione di un servizio.

Passando alla MainWindow vediamo che implementa una proprieta’ ViewModel decorata anch’essa con l’attributo Import.

clip_image010

Di seguito troviamo l’implementazione di un plugin che implementera’ l’interfaccia IPlugin, cioe’ il contratto da noi definito. In questo caso con l’attributo Export abbiamo anche specificato il tipo da esportare, o meglio il tipo di servizio che offre la nostra Composable Part.

Togliendo il typeof(IPlugin) e lasciando un semplice Export il CompositionContainer non sara’ in grado di risolvere le dipendenze.

clip_image012

Passiamo ora alla parte fondamentale…come fa MEF a capire a chi associare cosa?

clip_image014

Nel costruttore della view abbiamo creato il CompositionContainer passandogli come parametro un AssemblyCatalog (che tratteremo nei prossimi post) al quale abbiamo passato l’assembly corrente, in quanto all’interno del nostro progetto abbiamo il plugin che vogliamo ed inoltre il ViewModel della view

Infine abbiamo chiamato l’extension method ComposeParts passandogli l’istanza della nostra classe (anch’essa e’ una ComposablePart in quanto utilizza dei servizi cioe’ il ViewModel). Il ComposeParts risolvera’ le dipendenze (in realta’ se ne occupa il metodo Compose del container) ed avremo il nostro progetto bello e funzionante.

clip_image016

Ribbon, PRISM & RegionAdapterBase

Da qualche giorno ho iniziato ad utilizzare PRISM all’interno di un progetto universitario e visto che c’ero venerdi’ scorso ho partecipato al workshop tenuto da DotNetMarche su WPF, MVVM e UI Composition (a dir poco fantastico, soprattutto la cena :D ).

Per la UI ho scelto di utilizzare i WPF Ribbon Controls, visto che sono free, abbastanza documentati e anche se hanno qualche “magagna” in fondo funzionano bene.

Il mio intento e’ di fare in modo che ogni modulo dell’applicazione possa iniettare, oltre che alla view principale, anche un menu’, o meglio un RibbonTab, in quanto il menu’ di base della shell e’ un controllo Ribbon.

ribbon

ribbontab 

Mi sono cimentato in questa “impresa” prima di finir di leggere la Composite Application Guidance for WPF o meglio proprio una manciata di pagine prima della sezione sulle regions…

All’inizio, quindi, ho provato semplicemente ad aggiungere il menu’ alla MenuRegion (visto che ha funzionato con un TabControl per la main view perche’ non dovrebbe funzionare con il Ribbon? :D ) ed infatti non veniva visualizzato nulla.

Cosi’ ho chiesto a Paolo Possanzini alcune delucidazione e molto gentilmente mi ha spiegato a cosa servono i Region Adapters e quando utilizzarli!

Alla fine ho creato un RibbonRegionAdapter per la Ribbon

   1: public class RibbonRegionAdapter : RegionAdapterBase<Ribbon>
   2:     {
   3:         private Ribbon regionTarget;
   4:  
   5:         public RibbonRegionAdapter(IRegionBehaviorFactory factory)
   6:             : base(factory)
   7:         {
   8:         }
   9:  
  10:         protected override void Adapt(IRegion region, Ribbon regionTarget)
  11:         {
  12:             this.regionTarget = regionTarget;
  13:  
  14:             regionTarget.Tabs.Clear();
  15:             region.ActiveViews.CollectionChanged += new NotifyCollectionChangedEventHandler(OnActiveViewsChanged);
  16:         }
  17:  
  18:         protected override IRegion CreateRegion()
  19:         {
  20:             return new AllActiveRegion();
  21:         }
  22:  
  23:         private void OnActiveViewsChanged(object sender, NotifyCollectionChangedEventArgs e)
  24:         {
  25:             switch (e.Action)
  26:             {
  27:                 case NotifyCollectionChangedAction.Add:
  28:                     foreach (RibbonTab v in e.NewItems)
  29:                         regionTarget.Tabs.Add(v);
  30:                     break;
  31:                 case NotifyCollectionChangedAction.Remove:
  32:                     foreach (RibbonTab v in e.NewItems)
  33:                         regionTarget.Tabs.Remove(v);
  34:                     break;
  35:             }
  36:         }
  37:     }

ed ora tutto funziona correttamente (o quasi…ma questo e’ un altro discorso)…