tutti i motori per Inversion of Control, in un modo o nell’altro, offrono degli “extension point” che ci permettono di iniettare logica custom all’interno della *-pipeline del motore di IoC (ove * è una libera scelta tra registration, resolution, interception, etc..etc…).
“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:
[ImportEngineDefinition( "ExcelOpenXmlImportBasicEngineUniqueId", "Excel OpenXml (Basic)" )]
public class ExcelOpenXmlImportBasicEngine : IImportEngine
resta sempre il fatto che è una discreta rottura di “maroni” scrivere poi tutta la parte di reflection per andare a scoprire i metadati.
Facility to the rescue
Perché quindi non ipotizzare una cosa del tipo:
public interface IEngineDefinition
{
	String UniqueId { get; }
	String Name { get; }	
	String Description { get; }
}
 
[Topics.Radical.ComponentModel.Contract]
public interface IImportEngineDefinition : IEngineDefinition
{
 
}
e una relativa implementazione di base che tralascio vista la sua banalità… Smile fatto questo non ci resta che trovare un sistema per automatizzare il tutto:
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 )
				);
			}
		};
	}
}
Molto interessante Smile 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…
Questo ci consente di fare poi una cosa del genere:
public IEnumerable<IImportEngineDefinition> GetInstalledEngines()
{
	return container.ResolveAll<IImportEngineDefinition>();
}
il tutto semplicemente registrando la nostra facility:
container.AddFacility<EngineDefinitionBuilderFaclity>();
Figo Smile with tongue out
.m