MEF Recomposition

Il DirectoryWatcherCatalog introdotto in uno dei post precedenti ci introduce ad una caratteristica molto importante di MEF chiamata Recomposition.

Che cos’è?
La recomposition permette “a MEF” di aggiornare gli import quando vengono aggiunti nuovi export.

Per cui nel caso del DirectoryWatcherCatalog quando aggiungiamo una dll all’interno della directory monitorata, la lista di plugins (definita di seguito) dovrebbe essere aggiornata automaticamente da MEF.

   1: [ImportMany(typeof(IPlugin))]
   2: public ObservableCollection<IPlugin> Plugins { get; set; }

Dovrebbe?!? Si dovrebbe perchè quando aggiungiamo il plugin alla directory monitorata otteniamo una ChangeRejectedException

image

dove il messaggio di errore è

The composition remains unchanged. The changes were rejected because of the following error(s): The composition produced a single composition error. The root cause is provided below. Review the CompositionException.Errors property for more detailed information.

1) Change in exports prevented by non-recomposable import 'IntroductionToMEFSample.ViewModels.MainViewModel.Plugins (ContractName="MEFSample.Contracts.IPlugin")' on part 'IntroductionToMEFSample.ViewModels.MainViewModel'.

 

Perchè non funziona? E come facciamo a far funzionare tutto l’ambaradan?
Non funziona in quanto il comportamento standard di MEF non permette la recomposition e per far funzionare il tutto basta semplicemente settare la propietà AllowRecomposition dell’attributo Import/ImportMany a true

   1: [ImportMany(typeof(IPlugin), AllowRecomposition = true)]
   2: public ObservableCollection<IPlugin> Plugins { get; set; }

Se la classe che espone la proprietà Plugins implementa l’interfaccia IPartImportsSatisfiedNotification il metodo OnImportsSatisfied verrà chiamato ogni volta che avviene la recomposition. Un’altra maniera per sapere quando avviene la recomposition o più in generale quando cambiano gli Exports è sottoscrivere uno di questi due eventi del CompositionContainer

   1: /// <summary>
   2: ///     Occurs when the exports in the <see cref="ExportProvider"/>
   3: ///     have changed.
   4: /// </summary>
   5: public event EventHandler<ExportsChangeEventArgs> ExportsChanged;
   6:  
   7: /// <summary>
   8: ///     Occurs when the exports in the <see cref="ExportProvider"/>
   9: ///     are changing.
  10: /// </summary>
  11: public event EventHandler<ExportsChangeEventArgs> ExportsChanging;

Nei post a seguire parleremo di Metadata e se il tempo permette vi mostrerò il plugin manager che uso all’interno delle mie applicazioni!
Per cui stay tuned :D

InheritedExport

Leggendo qualche post qua e là e spulciando i sorgenti di MEF ho notato che oltre al classico ExportAttribute è presente anche InheritedExportAttribute!

Qual è la differenza tra i due?
Semplice! Con quest’ultimo possiamo decorare direttamente il nostro contratto

   1: [InheritedExport]
   2: public interface IPlugin
   3: {
   4:     string Name { get; set; }
   5:     string Description { get; set; }
   6:     string AuthorName { get; set; }
   7: }

senza dover decorare ogni plugin con ExportAttribute :)

   1: public class FirstPlugin : IPlugin
   2:     {
   3:         public FirstPlugin()
   4:         {
   5:             this.Name = "FirstPlugin";
   6:             this.Description = "FirstPlugin";
   7:             this.AuthorName = "Federico Degrandis";
   8:         }
   9:  
  10:         #region IPlugin Members
  11:  
  12:         public string Name { get; set; }
  13:  
  14:         public string Description { get; set; }
  15:  
  16:         public string AuthorName { get; set; }
  17:  
  18:         #endregion
  19:     }

Logicamente tutte le classi che implementano l’interfaccia IPlugin, all’interno degli assembly caricati dai catalog, saranno visti come composable part!

Buon MEF a tutti quanti :D