Windsor Container e gli interceptor

Con Windsor di CastleProject, il noto framework di IoC, è possibile aggiungere alla nostra applicazione un pizzico di AOP usando gli Interceptor, una facility che permette di intercettare la chiamata ai metodi e alle proprietà di una classe opportunamente configurata.

Per capire meglio come utilizzare la facility, analizziamo il classico degli esempi legati al mondo AOP: il logger.

Tipicamente, in una  normale applicazione, le operazioni di logging consistono nel fare chiamate ad una libreria dedicata alla scrittura (su file, su db, ecc...) del log. Le chiamate devono essere esplicitate a priori nei punti in cui si vuole loggare, mescolando il codice della nostra applicazione con quello dedicato al logging.

Con un interceptor le chiamate al logging possono essere spostate in una classe ad hoc e la nostra applicazione può essere sviluppata senza pensare al logging che verrà aggiunto in seguito senza però modificare ciò che abbiamo già scritto.

Windsor Container dispone di una facility deidcata all'interecettazione delle chiamate di alcune classi. Scrivere un interceptor significa scrivere una classe che implementa l'interfaccia IInterceptor:

public interface IInterceptor { void Intercept(IInvocation invocation); }

L'interfaccia dispone di un metodo (Intercept) che verrà chiamato da Windsor prima dell'esecuzione di un metodo.

Un'implementazione tipica di un logger basato sull'interceptor potrebbe essere questa:

public class Logger : IInterceptor { public void Intercept(IInvocation invocation) { Console.WriteLine("Sto per eseguire: {0}", invocation.Method.Name); invocation.Proceed(); Console.WriteLine("Ho finito l'esecuzione di: {0}", invocation.Method.Name); } }

L'implementazione del metodo Intercept logga (in questo caso su console) prima dell'esecuzione del metodo e dopo l'esecuzione del metodo che avviene con la chiamata a invocation.Proceed().

Una volta implementato, il Logger va registrato come componente nel file di configurazione di Windsor, quindi nella sezione Components aggiungeremo:

<component id="logInterceptor" service="CodicePlastico.WindsorInterceptor.Logger, CodicePlastico.WindsorInterceptor" type="CodicePlastico.WindsorInterceptor.Logger, CodicePlastico.WindsorInterceptor" />

 

A questo punto l'interceptor è registrato e può essere utilizzato per intercettare le chiamate su un tipo anch'esso registrato. Supponiamo di voler loggare le chiamate ad un Repository che si occupa della persistenza di un oggetto Customer:

public class CustomerRepository : ICustomerRepository { public IList<Customer> Read() { Console.WriteLine("Load Customer from Db"); return null; } public void Save(Customer item) { Console.WriteLine("Save customer to Db"); } public void Delete(Customer item) { Console.WriteLine("Delete Customer"); } }

 

Anche l'oggetto CustomerRepository deve essere registrato nel file di configurazione:

<component id="customerRepository" service="CodicePlastico.WindsorInterceptor.ICustomerRepository, CodicePlastico.WindsorInterceptor" type="CodicePlastico.WindsorInterceptor.CustomerRepository, CodicePlastico.WindsorInterceptor" />

Questa è una registrazione standard per il container, se voglio fare in modo che l'interceptor registrato prima intercetti le chiamate a CustomerRepository devo aggiungere un'informazione al file di configurazione:

<component id="customerRepository" service="CodicePlastico.WindsorInterceptor.ICustomerRepository, CodicePlastico.WindsorInterceptor" type="CodicePlastico.WindsorInterceptor.CustomerRepository, CodicePlastico.WindsorInterceptor"> <interceptors> <interceptor>${logInterceptor}</interceptor> </interceptors> </component>

Ho semplicemente aggiunto alla registrazione del componente l'informazione che l'interceptor "logInterceptor" si occuperà di interecettare le chiamate fatte alle istanze del CustomerRepository.

In questo modo il seguente snippet di codice:

IWindsorContainer container = new WindsorContainer(new XmlInterpreter("windsor.xml")); ICustomerRepository repo = container.Resolve<ICustomerRepository>(); repo.Read();

 

Genererà il seguente output:

Sto per eseguire: Read Load Customer from Db Ho finito l'esecuzione di: Read

La bellezza del codice visto fin'ora sta nel fatto che l'aspetto legato al logging è completamente ortogonale al codice dell'applicazione ed è configurabile tramite un comodo file xml.

Potete scaricare l'esempio completo qui: http://www.codiceplastico.com/files/Codiceplastico.WindsorInterceptor.zip

Per chi non conoscesse Windsor Container qui (...) trovate una serie di articoli scritti dal nostro Simone Busoli.

 

 

Print | posted on Saturday, January 19, 2008 4:17 PM