Quasi tutti ormai anche nel mondo .NET hanno sentito parlare di Inversion of Control e Dependency Injection. Molti hanno usato uno dei framework disponibili (Castle, Spring, ecc...) e ne hanno colto i vantaggi.
Però la quasi totalità degli esempi e tutorial che si trovano in rete mostrano esempi in cui la costruzione di un oggetto viene fatta dal Fx di IoC tramite il costruttore.
In questi giorni mi serviva che gli oggetti venissero costruiti dal framework di IoC (in questo caso Castle) utilizzando però una classe factory invece che il loro costruttore.
Dopo un po' di ricerche all'interno del wiki ho trovato una Facility che permette di creare oggetti passando da una classe factory.
L'implementazione è come sempre molto semplice (una volta trovata!) e si basa tutta sulla corretta configurazione di Castle.
Consideriamo un esempio abbastanza tipico. In un'applicazione che utilizza NHibernate e che gestisce la sessione come mostrato nel post di Mario è auspicabile che i vari Presenter non abbiamo un riferimento diretto alla classe che crea gli oggetti SessionImpl, oltre a questo va ricordato che l'oggetto SessionImpl non può essere istanziato direttamente con l'operatore new ma deve essere richiesto tramite la chiamata ad un metodo della classe SessionFactoryImpl.
Quindi se volessi fare il tutto con un Fx di IoC devo istruirlo ad utilizzare una factory invece che utilizzare il costruttore di SessionImpl.
Vediamo una possibile soluzione (ho semplificato per chiarire meglio l'uso di IoC).
Incapsulando la SessionFactory nella classe NHSessionFactory, nel file di configurazione ho registrato alcuni componenti e una facility:
<castle>
<facilities>
<facility id="factorysupport" type="Castle.Facilities.FactorySupport.FactorySupportFacility, Castle.Microkernel"/>
</facilities>
<components>
<component id="sessionFactory" type="MyProject.Data.NHSessionFactory, MyProject.Data"/>
<component id="sessionProvider" type="NHibernate.ISession, NHibernate" factoryId="sessionFactory" factoryCreate="GetSession" />
<component id="persistenceContext" service="MyProject.Stuff.PersistenceContext, MyProject.Stuff" type="MyProject.Stuff.PersistenceContext, MyProject.Stuff"/>
</components>
</castle>
La facility registra la classe per la costruzione di oggetti tramite factory.
In seguito registro la classe factory che si dovrà occupare della creazione degli oggetti, la classe è NHSessionFactory è come dice il nome è la classe ci si occupa di instazione oggetti ISession.
Gli oggetti che Castle ritorna sono registrati subito dopo, tramite il componente sessionProvider. Qui sto dicendo che gli oggetti di tipo ISession devono essere istanziati chiedendoli al componente sessionFactory (attributo facoryId) tramite il metodo GetSession (attributo factoryCreate).
Infine registro l'oggetto PersistenceContext che rappresenta il contesto di persistenza (legato al presenter) tramite il quale NH gestisce il ciclo di vita degli oggetti e che riceve nel costruttore un riferimento ad un oggetto ISession.
A livello di codice bastano due righe per ottenere l'istanza:
IWindsorContainer container = new WindsorContainer(new XmlInterpreter(new ConfigResource("castle")));
PersistenceContext ctx = container.Resolve<PersistenceContext>();
Cosa accade dietro le quinte?
Castle si inizializza leggendo dal file di configurazione i componenti. Gli viene chiesto di risolvere un'instanza di PersistenceContext il quale necessita nel costruttore di un'instanza di ISession la quale a sua volta deve essere richiesta alla factory NHSessionFactory tramite il metodo GetSession.
Semplice no?
NHibernate