Windsor e la potenza delle facility
“Descriptors”
Immaginiamo uno scenario abbastanza comune: avete un modello a plugin in cui la scelta del plugin da usare è demandata all’utente, dovete quindi fornire all’utente una lista “descrittiva” dei plugin disponibili.
Di primo acchito quello che faremmo è definire i nostri plugin e poi definire una gerarchia di classi atta a descrivere i nostri plugin, il motivo è abbastanza semplice: non vogliamo istanziare i plugin al solo scopo di scoprire chi e cosa sono, è ovvio che però questa è una discreta menata di cui faremmo volentieri a meno…
Se ci pensiamo bene abbiamo già qualcosa di disponibile da sempre nel framework che ci permette di agganciare metadati (del resto una descrizione quello è) ad un tipo, quindi la seconda ipotesi è fare una cosa del tipo:
resta sempre il fatto che è una discreta rottura di “maroni” scrivere poi tutta la parte di reflection per andare a scoprire i metadati.[ImportEngineDefinition( "ExcelOpenXmlImportBasicEngineUniqueId", "Excel OpenXml (Basic)" )] public class ExcelOpenXmlImportBasicEngine : IImportEngine
Facility to the rescue
Perché quindi non ipotizzare una cosa del tipo:
e una relativa implementazione di base che tralascio vista la sua banalità… fatto questo non ci resta che trovare un sistema per automatizzare il tutto:public interface IEngineDefinition { String UniqueId { get; } String Name { get; } String Description { get; } } [Topics.Radical.ComponentModel.Contract] public interface IImportEngineDefinition : IEngineDefinition { }
Molto interessante chiediamo a Windsor di notificarci ogni volta che un nuovo componente viene registrato, se è del tipo che ci interessa ci limitiamo ad estrarre i metadati che ci servono e a costruire la descrizione registrandola poi nel container stesso…public class EngineDefinitionBuilderFaclity : AbstractFacility { protected override void Init() { this.Kernel.ComponentRegistered += ( s, e ) => { EngineDefinition def = null; Type defType = null; if( e.Service.Is<IImportEngine>() ) { var att = e.ComponentModel.Implementation.GetAttribute<ImportEngineDefinitionAttribute>(); def = new ImportEngineDefinition( att.UniqueId, att.Name, att.Description ); defType = typeof( IImportEngineDefinition ); } else if( e.Service.Is<IExportEngine>() ) { var att = e.ComponentModel.Implementation.GetAttribute<ExportEngineDefinitionAttribute>(); def = new ExportEngineDefinition( att.UniqueId, att.Name, att.Description, att.SupportedDataTypes ); defType = typeof( IExportEngineDefinition ); } if( def != null ) { this.Kernel.Register ( Component.For( defType ) .Named( "EngineDefinition-" + def.UniqueId ) .Instance( def ) .LifeStyle.Is( Castle.Core.LifestyleType.Singleton ) ); } }; } }
Questo ci consente di fare poi una cosa del genere:
il tutto semplicemente registrando la nostra facility:public IEnumerable<IImportEngineDefinition> GetInstalledEngines() { return container.ResolveAll<IImportEngineDefinition>(); }
container.AddFacility<EngineDefinitionBuilderFaclity>();
Figo .m