Qualche tempo fa avevo “mostrato” che era possibile caricare dei plugin su un AppDomain secondario utilizzando MEF. Effettivamente è possibile l’unico (per modo di dire) problema è che dobbiamo riscriverci tutto il model di MEF…
Quali sono le principali problematiche?
1) Dobbiamo utilizzare un altro modello ad attributi che non sia quello di MEF, per cui abbiamo bisogno di creare dei nuovi attributi con cui decorare i plugin
2) Il punto 1) comporta la scrittura di nuovi catalogs per effettuare il discovery delle ComposablePart che utilizzano i nuovi attributi
3) Ci serve un “qualcosa” che istanzi i plugin su un AppDomain secondario…e ci vuole un “qualcos’altro” che ci crei il nuovo AppDomain
4) Il tutto dovrebbe essere thread-safe
5) I plugin devono implementare MarshallByRefObject
Qualche tempo fa, i tipi di MefContrib avevano riscritto un model generico che poteva essere configurato tramite attributi, fluent interface e file xml. La cosa era a dir poco fantastica solo che ora è stato rimosso quasi tutto in quanto non riuscivano a supportare, senza grandi sforzi, l’evoluzione di MEF. In compenso hanno aggiunto qualche altra “figata” che ci potrebbe venire incontro e semplificare la vita! Alla fine, però, per raggiungere il mio scopo, ho utilizzato una versione precedente di MefContrib contenente, ancora, il model generico (ho voluto farmi del male)
Nei post che seguiranno non vedremo passo per passo come scrivere un nostro model per MEF in quanto lo trovo abbastanza “noioso” ed impegnativo, per cui ci concentreremo sui concetti più importanti.
Prima di tutto non utilizzeremo più l’attributo ExportAttribute per definire un plugin ma bensì ExportableAttribute (l’uso è assolutamente identico)
Tutti i catalogs di MEF utilizzano l’attributo ExportAttribute per effettuare il discovery dei plugin (composable part) per cui se vogliamo “far vedere” al CompositionContainer i plugin dovremo creare uno o più catalog personalizzati. L’idea è di avere un DirectoryCatalog (customizzato) che ci permetta di specificare una cartella da cui recuperare le varie dll ed un AssemblyCatalog (sempre customizzato) che verrà utilizzato dal primo per analizzare quali assembly contengono effettivamente dei plugin.
Per creare un catalog custom dobbiamo ereditare da ComposablePartCatalog che richiede di implementare la proprietà Parts
Questa proprietà verrà utilizzata dal CompositionContainer per accedere alle varie ComposablePart (ComposablePartDefinition oltre a descrivere la relativa ComposablePart ha un metodo CreatePart che si occupa di crearla).
Il CompositionContainer quando andrà ad associare gli export con i relativi import chiamerà il metodo CreatePart e poi il metodo GetExportedValue della classe ComposablePart. Quindi la classe ComposablePart si occupa di istanziare il plugin!
Problemi:
1) ComposablePartDefinition e ComposablePart sono astratte per cui dobbiamo implementarle
2) Ci serve un “qualcosa” che istanzi il plugin sull’AppDomain secondario
Partiamo dal problema 2)
Questo qualcosa è un’implementazione dell’interfaccia ILocalLoader
Il DirectoryCatalog che abbiamo creato, nel costruttore, richiede un’implementazione di ILocalLoader…questa implementazione poi verrà passata all’AssemblyCatalog, che a sua volta la passerà alle ComposablePartDefinition create in base all’assembly analizzato. Alla fine ILocalLoader verrà utilizzato da ComposablePart per istanziare il tipo (plugin) richiesto.
ILocalLoader viene anche utilizzato dal DirectoryCatalog, o meglio dalla nostra implementazione, in quanto, quando viene fatto l’unload dell’AppDomain secondario, il DirectoryCatalog deve “eliminare” tutte le ComposablePartDefinition che ha memorizzato!
Il funzionamento di ILocalLoader verrà trattato in maniera dettagliata nel prossimo post, ora vediamo il punto 1)
Come abbiamo accennato, ComposablePartDefinition e ComposablePart sono astratte, per cui dobbiamo implementarle.
ComposablePartDefinition ci “obbliga” ad implementare le proprietà ExportDefinitions, ImportDefinitions ed il metodo CreatePart.
MefContrib contiene, forse meglio dire conteneva, un’implementazione di ComposablePartDefinition chiamata ProviderComposablePartDefinition. Quest’ultima è stata modificata leggermente per adattarla ai nostri bisogni
Le proprietà ExportDefinitions ed ImportDefinitions ritornano i relativi parametri specificati nel costruttore ed il metodo CreatePart non fa altro che ritornare un’implementazione di ComposablePart, sempre presente all’interno di MefContrib, chiamata ProviderComposablePart.
ProviderComposablePart accederà a ILocalLoader tramite l’istanza di ProviderComposablePartDefinition passata e creerà il plugin (quando verrà richiesto dal CompositionContainer).
Nel prossimo post, vedremo come ProviderComposablePart utilizza ILocalLoader per istanziare il plugin e come viene istanziato su un AppDomain secondario.
Per qualsiasi domanda, dubbio, perplessità non esitate a contattarmi
Stay tuned!
PS: I sorgenti verranno rilasciati asap
posted @ giovedì 28 ottobre 2010 00:13