Thursday, March 31, 2011

Windsor e la potenza delle facility

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

Wednesday, March 30, 2011

Visual Studio LightSwitch (Beta 2) primo impatto…

…read my mouth : F I G A T A  P A Z Z E S C A
Ho un’amica che ha un piccolo negozio ed è stufa di gestire tutto a manina e mi chiede se le posso realizzare qualcosa ad esempio con Access, mi si sono rizzati i peli del **** al solo pensiero di smanettare con VBA.
Memore di una piacevole chiacchierata, al Summit di quest’anno, con Alessandro Del Sole ho deciso di provare Visual Studio LightSwitch e praticamente in circa 40 minuti avevo l’applicazione fatta e finita. Applicazione neanche tanto semplice, con un discreto ObjectModel e con tante belle regoline e regolette.
Insomma, direi decisamente positivo come primo impatto Smile with tongue out
Chi indovina la citazione di cui sopra?
.m

Tuesday, March 29, 2011

MEF e Windsor: l’unione fa la forza :-)

Avevo dato uno sguardo a MEF tempo fa, giusto uno sguardo. L’ho poi usato, in un piccolo progetto, per la gestione di un sistema basato su plugin, la morte sua, ed ero rimasto piacevolmente sorpreso dalla semplicità e rapidità con cui avevo messo in piedi la soluzione.
Una cosa che però mi ha sempre lasciato un po’ perplesso è che MEF non è un framework di Inversion of Control, come ad esempio il mio amato Castle Windsor, ma piuttosto è un motore per fare “component composition”, è possibile usarlo come motore per IoC ma vi perdete alcune delle feature più interessanti che tutti i motori per IoC mettono a disposizione. Un’altra importante differenza è che MEF ragiona sulle istanze e non sui tipi, non “registrate” un tipo ma piuttosto descrivete (con dei metadati) come volete che gli oggetti (e non le classi) vengano composte a runtime.
Una coesistenza pacifica e proficua
Q: è possibile trarre il maggior beneficio dai due mondi?
A: ovviamente la risposta è si, altrimenti che scriverei a fare? Smile
Scenario: Diciamo che abbiamo una semplice applicazione che fa largo uso di “UI composition” e che per tutta una serie di buoni motivi ha necessariamente bisogno di molte delle feature esposte da un motore di IoC e non erogate da MEF, dall’altra parte però ci rendiamo conto che è ci sono alcune cose che sono una vera menata colossale da realizzare, non tanto difficili quando noiose:
  • il discovery dei moduli installati;
  • il bootstrap dei singoli moduli;
  • la condivisione tra l’applicazione e i moduli del “container” (che ovviamente deve essere uno ed uno solo);
Quindi perché non pensare ad una cosa del tipo:
public sealed class ApplicationBootstrapper : WindsorApplicationBootstrapper
{
	[ImportMany]
	public IEnumerable<IWindsorInstaller> Installers { get; set; }
 
	[Export]
	[Export( typeof( IServiceProvider ) )]
	public IWindsorContainer Container { get; set; }
 
	CompositionContainer MefContainer;
 
	protected override IWindsorContainer CreateServiceProvider()
	{
		this.Container = base.CreateServiceProvider();
 
		var catalog = new DirectoryCatalog( Environment.CurrentDirectory );
		this.MefContainer = new CompositionContainer( catalog );
 
		return this.Container;
	}
 
	protected override void InitializeEnvironment( IWindsorContainer container )
	{
		container.Register
		(
			Component.For<CompositionContainer>()
				.Instance( this.MefContainer )
		);
 
		this.MefContainer.ComposeParts( this );
 
		container.Install( this.Installers.ToArray() );
 
		base.InitializeEnvironment( container );
	}
}
L’ApplicationBootstrapper è il “main entry point” della nostra applicazione (deriva da un WindsorApplicationBootstrapper che ha l’unico scopo di ridurre un po’ la frizione):
  • all’atto della creazione del container fa due cose:
    • Imposta una proprietà pubblica con l’istanza del container appena creato, proprietà decorata con un attributo di MEF che identifica che quella proprietà è “esportabile”;
    • costruisce un’istanza di un CompositionContainer che è MEF;
  • All’atto della configurazione:
    • registra in Windsor MEF stesso al fine di renderlo disponibile al mondo;
    • chiede a MEF di comporre se stesso: questo scatena il discovery da parte di MEF di tutti i tipi importabili e di tutti i tipi esportabili;
    • A seguito del discovery sappiamo che MEF avrà popolato la proprietà pubblica “Installers” con tutte le istanze “esportate” di tipo IWindsorInstaller;
    • possiamo quindi chiedere a Windsor di completare il processo di configurazione;
In uno scenario di questo genere un modulo cosa deve fare? semplicemente ad esempio questo:
[Export( typeof( IWindsorInstaller ) )]
public class PresentationInstaller : IWindsorInstaller
{
	public void Install( IWindsorContainer container, IConfigurationStore store )
	{
		var descriptor = AllTypes.FromAssemblyNamed( "MyAssembly" );
 
		container.Register
		(
			descriptor.Where( t => t.Namespace.IsLike( "*.Presentation" ) )
				.Unless( t => t.IsAbstract || t.IsNested )
				.Configure( d =>
				{
					if( d.Implementation.Name.IsLike( "*Shell*" ) )
					{
						d.LifeStyle.Is( LifestyleType.Singleton );
					}
					else
					{
						d.LifeStyle.Is( LifestyleType.Transient );
					}
				} )
				.WithService.Select( ( type, baseTypes ) =>
				{
					var all = type.GetInterfaces()
							.Where( i => i.IsAttributeDefined<ContractAttribute>() );
 
					return all;
				} )
		);
 
		container.Register
		(
			descriptor.Where( t => t.Namespace.IsLike( "*.Presentation.Model" ) )
				.Unless( t => t.IsAbstract || t.IsNested )
				.Configure( d => d.LifeStyle.Is( LifestyleType.Transient ) )
		);
	}
}
Definiamo semplicemente un nuovo “installer” per Windsor e lo marchiamo come “esportabile” e MEF farà tutto il resto.
Il tutto è di una semplicità disarmante e riduce drasticamente la complessità, ma ancor di più la quantità di codice ripetitivo e noioso, della realizzazione di un’applicazione modulare; non solo ma sulla falsariga di questo esempio si aprono scenari molto interessanti.
.m

Tuesday, March 22, 2011

Inversion of Control on the Phone: IPuzzleContainer

Quando si è abituati a scrivere codice seguendo un certo stile è difficile cambiare approccio, soprattutto se:
  • lo stile che siamo abituati ad usare è efficace;
  • il cambiamento ci obbliga ad essere meno efficaci;
Entrambe le affermazioni sono vere se siete abituati a scrivere codice seguendo i principi di Inversion of Control e Dependency Injection e vi trovate ad un certo punto a sviluppare per una piattaforma per cui non esiste un framework per Inversion of Control.
Windows Phone 7
In questi giorni mi sto dilettando con lo sviluppo di un’applicazione per Windows Phone 7, è per uso personale e la sto facendo nel tempo libero sfruttando la cosa per studiare una piattaforma con cui avevo giochicchiato all’inizio e poi abbandonato per altri lidi Smile. L’applicazione in se e per se non fa nulla di trascendentale e non è oggetto del post, l’intenzione è però quella di realizzarla seguendo lo stesso stile di programmazione che mi accompagna da anni nello sviluppo di applicazioni Wpf e Silverlight. In particolare quello che mi interessa in questo momento è:
  • Model View ViewModel: con il solito approccio un po’ “talebano” finalizzato a spingermi al limite delle possibilità offerte dalla piattaforma;
  • Inversion of Control e Dependency Injection: principalmente al fine di non perdere sane e buone abitudini e non ultimo al fine di poter introdurre con semplicità Unit Test su Windows Phone 7;
Per M-V-VM direi che non c’è problema s’è solo da “litigare” un po’ con le limitazioni di Silverlight per Wp7 soprattutto per me che sono ben abituato dalla potenza di Wpf; il problema vero è sorto quando mi sono messo a cercare un’implementazione di un motore per IoC per il telefono, ammetto che non mi sono dannato molto ma non ho trovato nulla che fosse degno di nota. C’è una versione di Castle Windsor compilata per Silverlight 3 (e una anche per Silverlight 4) ma in entrambi i casi l’applicazione compila correttamente ma “sbomba” clamorosamente all’avvio.
IPuzzleContainer
Al che mi sono detto: beh…facciamocelo Smile, che ci vorrà mai Smile with tongue out
In effetti un paio d’ore di lavoro, più un’oretta di refactoring per renderlo molto simile a Castle Windsor nell’uso hanno prodotto questo:
var container = new PuzzleContainer();
 
container.Register
(
	EntryBuilder.For<Dispatcher>()
		.UsingInstance( Deployment.Current.Dispatcher )
);
 
container.Register
(
	EntryBuilder.For<IDispatcher>()
		.ImplementedBy<SilverlightDispatcher>()
);
 
container.Register
(
	EntryBuilder.For<IMessageBroker>()
		.ImplementedBy<MessageBroker>()
);
Come si può notare la sintassi per la registrazione dei componenti è molto simile proprio a quella di Castle Windsor, in questo esempio ci limitiamo a registrare dei contratti e la loro implementazione concreta, nel primo caso abbiamo già una “live instance” e quindi registriamo quella. IMessageBroker ha una dipendenza (nel costruttore, quindi mandatory) proprio sui due tipi registrati in precedenza, IPuzzleContainer risolverà per noi le dipendenze all’atto della risoluzione del broker.
Naturalmente abbiamo anche il supporto per i tipi concreti e le factory:
container.Register
(
	EntryBuilder.For<LifecycleAwareService>()
);
 
container.Register
(
	EntryBuilder.For<IsolatedStorageSettings>().UsingFactory( () =>
	{
		return IsolatedStorageSettings.ApplicationSettings;
	} )
);
LifecycleAwareService non implementa nessuna interfaccia e viene registrato direttamente, mentre nel secondo caso usiamo una factory per risolvere il tipo concreto.
Sempre per mantenermi allineato con Castle c’è anche il supporto per il Lifestyle che di default è Singleton, ma in fase di registrazione può essere cambiato a Transient, non c’è nessun supporto per altri tipi di Lifestyle e sinceramente non ne vedo la necessità. Se proprio fosse necessario si potrebbe ipotizzare un Threaded Lifestyle, cosa peraltro tutto tranne che difficile.
L’uso è altrettanto semplice:
var lifeSvc = container.Resolve<LifecycleAwareService>();
Triviale direi. Un paio di note per concludere:
  • Funziona? si;
  • E’ affidabile: non ne ho la più pallida idea Smile
  • E’ performante: a naso sembrerebbe di si, ma non ho fatto la benché minima misurazione;
  • Dove lo trovo? Radical for Windows Phone 7…
  • Quando? a breve Smile non ho ancora fatto check-in dei sorgenti, mi piacerebbe scrivere qualche Unit Test prima;
  • Il prossimo step? il supporto per le facility sempre in stile Castle Windsor;
Insomma quando sarà “su” fateci quello che volete ma non sparate sul pianista Smile with tongue out
.m