MEF & AppDomains

Da un pò di tempo sto cercando di far caricare a MEF gli exports (plugins) in un AppDomain secondario (con tutti i pro e contro che questo comporta) e pian piano qualcosa sta venendo fuori...



Stay tuned...

Filtering Exports with FilteredCatalog

MEFContrib è un progetto creato da Glenn Block ed altre persone note in cui inseriscono alcune chicche che possono essere utili quando si utilizza MEF ad esempio:

1) un catalog che supporta gli open generics (MEF di default supporta solo i closed generics)
2) un catalog che ci permette di filtrare gli exports
3) integrazione con Unity
4) un “botto” di cose che non ho la minima idea a cosa servano :)

In questo post vedremo un esempio che mostra un utilizzo del FilteredCatalog!

Scenario:

Abbiamo un’interfaccia ILogger che rappresenta un logger generico che ci permette di loggare un exception


Abbiamo un paio di implementazioni, molto farlocche, e vogliamo che solo quella “marcata come default” venga importata!

Soluzioni:

1) Chiediamo a MEF di importare tutte le implementazioni di ILogger e poi ce le filtriamo noi
2) Utilizziamo il FilteredCatalog che ci mette a disposizione MEFContrib

Mi sembra ovvio, visto il titolo del post, che adotteremo la seconda soluzione :)

FilteredCatalog ci offre due costruttori:


Il primo parametro, uguale per entrambi i costruttori, è il catalog da cui verranno ricavati gli exports, se abbiamo più catalogs possiamo usare un AggregateCatalog. Il secondo parametro è il nostro filtro, possiamo specificare una lambda expression oppure implementare l’interfaccia IFilter. MEFContrib offre due implementazioni di IFilter, una che ci permette di filtrare sui metadata o meglio su PartMetadataAttribute, poi vedremo cos’è, e l’altro che ci dice se la composable part definisce una determinata CreationPolicy.

Di seguito vediamo i due logger; entrambi implementano ILogger, sono marcati con l’attributo Export e PartMetadata. Quest’ultimo ci consente di assegnare alla ComposablePart (quel famoso oggetto che può contenere imports e exports e con cui lavorano i catalogs) dei metadata (informazioni aggiuntive). In questo caso creiamo un nuovo metadata, “Logger”, e sul primo logger lo settiamo a true mentre sul secondo a false.


PartMetadata verrà utilizzato dal FilteredCatalog, ed in particolare dall’implementazione che abbiamo utilizzato di IFilter per ottenere solo un’implementazione di ILogger e cioè quella di default.

Quando creiamo il FilteredCatalog, oltre al catalog da dove recuperare gli exports su cui filtrare, gli passiamo un’istanza di ContainsMetadata che non fa altro che controllare se
il metadata specificato, “Logger” in questo caso, esista e se il valore corrispondente è uguale a true.


Nei prossimi post vedremo altre cose interessanti di MEFContrib, per poi capire come caricare i nostri plugin/export su un AppDomain secondario ;)

ExportFactory for Desktop Applications

ExportFactory<T> è molto simile alla classe Lazy<T>, cambiano solo due cosucce:

  1.  Quando accediamo alla proprietà Value di Lazy<T> l’istanza dell’oggetto che ci viene ritornato è sempre la stessa, per cui l’oggeto viene creata una sola volta. ExportFactory<T> al posto della proprietà Value ci mette a disposizione il metodo CreateExport() che ci ritorna ogni volta una nuova istanza.
  2. ExportFactory<T> è disponibile solo per Silverlight :)

E se lo volessimo usare in una Desktop Application?
E’ possibile scaricare MEF 2 Preview 1 da codeplex ed usare ExportFactory<T> tranquillamente nelle nostre applicazioni Desktop.

Una volta referenziato l’assembly System.ComponentModel.Composition.Codeplex.dll potete importare i vostri plugin usando ExportFactory<T> esattamente come Lazy<T>


Però, per accedere al plugin, non si userà più la proprietà Value come con Lazy<T> ma bensì il metodo CreateExport() che ritorna un ExportLifetimeContext<T> e tramite la proprietà Value possiamo accedere all’istanza del plugin. ExportLifetimeContext<T> inoltre implementa l’interfaccia IDisposable così da poter rilasciare la memoria quando la composable part (il nostro plugin) non ci serve più.

Come possiamo vedere i due Object ID sono differenti per cui le due istanze create da CreateExport() sono diverse!