LAB 02 Lab Implementando Desacoplamiento IoC DI Con UNITY

21
Page | 1 Hands-On Lab LAB 02 - Implementando desacoplamiento IoC DI con UNITY Objetivos El objetivo de este Lab es analizar de forma práctica los diferentes puntos de desarrollo relacionados con el desacoplamiento de componentes mediante Inyección de Dependencias con Microsoft UNITY. La mayoría de los siguientes LABs están relacionados con la implementación de la aplicación ejemplo NLAYERAPP ( http://microsoftnlayerapp.codeplex.com/ ). Introducción a Unity El Application Block denominado Unity (implementado por Microsoft Patterns & Practices ), es un contenedor de inyección de dependencias extensible y ligero (Unity no es un gran framework pesado). Soporta inyección en el constructor, inyección de propiedades, inyección en llamadas a métodos y contenedores anidados. Básicamente, Unity es un contenedor donde podemos registrar tipos (clases, interfaces) y también mapeos entre dichos tipos (como un mapeo de un interfaz hacia una clase) y además el contenedor de Unity puede instanciar bajo demanda los tipos concretos requeridos.

Transcript of LAB 02 Lab Implementando Desacoplamiento IoC DI Con UNITY

Page 1: LAB 02 Lab Implementando Desacoplamiento IoC DI Con UNITY

Page | 1

Hands-On Lab

LAB 02 - Implementando desacoplamiento IoC DI con UNITY

Objetivos

El objetivo de este Lab es analizar de forma práctica los diferentes puntos de desarrollo relacionados con

el desacoplamiento de componentes mediante Inyección de Dependencias con Microsoft UNITY.

La mayoría de los siguientes LABs están relacionados con la implementación de la aplicación ejemplo

NLAYERAPP (http://microsoftnlayerapp.codeplex.com/ ).

Introducción a Unity

El Application Block denominado Unity (implementado por Microsoft Patterns & Practices), es un

contenedor de inyección de dependencias extensible y ligero (Unity no es un gran framework pesado).

Soporta inyección en el constructor, inyección de propiedades, inyección en llamadas a métodos y

contenedores anidados.

Básicamente, Unity es un contenedor donde podemos registrar tipos (clases, interfaces) y también

mapeos entre dichos tipos (como un mapeo de un interfaz hacia una clase) y además el contenedor de

Unity puede instanciar bajo demanda los tipos concretos requeridos.

Page 2: LAB 02 Lab Implementando Desacoplamiento IoC DI Con UNITY

Page | 2

Unity está disponible como un download público desde el site de Microsoft (es gratuito) y también está

incluido en la Enterprise Library 4.0/5.0 y en PRISM (Composite Applications Framework), los cuales

hacen uso extensivo de Unity.

Para hacer uso de Unity, normalmente registramos tipos y mapeos en un contenedor de forma que

especificamos las dependencias entre interfaces, clases base y tipos concretos de objetos. Podemos

definir estos registros y mapeos directamente por código fuente o bien, como normalmente se hará en

una aplicación real, mediante XML de ficheros de configuración. También se puede especificar inyección

de objetos en nuestras propias clases haciendo uso de atributos que indican las propiedades y métodos

que requieren inyección de objetos dependientes, así como los objetos especificados en los paráme tros

del constructor de una clase, que se inyectan automáticamente.

Unity proporciona las siguientes ventajas al desarrollo de aplicaciones:

- Soporta abstracción de requerimientos; esto permite a los desarrolladores el especificar dependencias en tiempo de ejecución o en configuración y simplifica la gestión de aspectos horizontales (crosscutting concerns), como puede ser el realizar pruebas unitarias contra mocks y

stubs, o contra los objetos reales de la aplicación.

- Proporciona una creación de objetos simplificada, especialmente con estructuras de objetos jerárquicos con dependencias, lo cual simplifica el código de la aplicación.

- Aumenta la flexibilidad al trasladar la configuración de los componentes al contenedor IoC.

Proporciona una capacidad de localización de servicios; esto permite a los clientes el guardar o cachear

el contenedor. Es por ejemplo especialmente útil en aplicaciones web ASP.NET donde los

desarrolladores pueden persistir el contenedor en la sesión o aplicación ASP.NET.

Page 3: LAB 02 Lab Implementando Desacoplamiento IoC DI Con UNITY

Page | 3

Ejercicio 1: Registro de Tipos en

contenedor de UNITY

Como ejemplo de uso de los métodos RegisterType y Resolve, a continuación realizamos un registro de

un mapeo de un interfaz llamado ICustomerService y especificamos que el contenedor debe devolver

una instancia de la clase CustomerService (la cual tendrá implementado el interfaz ICustomerService).

C#

//Registro de tipos en Contenedor de UNITY

IUnityContainer container = new UnityContainer();

container.RegisterType<ICustomerManagementService, CustomerManagementService>();

...

...

//Resolución de tipo a partir de Interfaz

ICustomerManagementService customerSrv = container.Resolve<IICustomerManagementService>();

Como posibilidad adicional, en la versión final de aplicación, el registro de clases, interfaces y mapeos en

el contenedor, se puede realizar de forma declarativa en el XML de los ficheros de configuración,

quedando completamente desacoplado. Sin embargo, tal y como se muestra en las líneas de código

anteriores, durante el desarrollo probamente es más cómodo realizarlo de forma ‘Hard-coded’, pues así

los errores tipográficos se detectarán en tiempo de compilación en lugar de en tiempo de ejecución

(como pasa con el XML).

Con respecto al código anterior, la línea que siempre estará en el código de la aplicación, sería la que

instancia propiamente el objeto resolviendo la clase que debe utilizarse mediante el contenedor, es

decir, la llamada al método Resolve() (Independientemente de si se realiza el registro de tipos por XML

o ‘Hard-Coded’).

Page 4: LAB 02 Lab Implementando Desacoplamiento IoC DI Con UNITY

Page | 4

Paso 1 – Analizar el Registro de Tipos (Clases e Interfaces) en la aplicación NLAYERSAMPLEAPP.

1. Abrir el fichero ‘IoCUnityContainer.cs’ del proyecto Infrastructure.CrossCutting.IoC:

2. Desplegar la sección ‘Private Methods’ dentro de dicho fichero:

3. Analizar los distintos registros de tipos realizados en el método

‘ConfigureRootContainer(IUnityContainer container)’:

Page 5: LAB 02 Lab Implementando Desacoplamiento IoC DI Con UNITY

Page | 5

4. El LifeTimeManager es el tipo de instanciación de clase que se realizará. En la mayoría de los

casos (Repositorios, Servicios, etc.) y por defecto si no se le especifica el LifeTimeManager a

Unity, la forma de instanciación normal será TransientLifetimeManager, es decir, un objeto

único por cada referencia. Pero en algunos casos puede interesarnos otros tipos de

instanciación como del estilo a ‘singleton’, compartidos por contextos de ejecución, etc.

Podemos tener los siguientes tipos de ‘vida’ de objetos instanciados:

o TransientLifetimeManager: Este LifeTimeManager asegura que las instancias de objetos

se crean nuevas cada vez (no reutilización de objetos entre diferentes llamadas).

o PerResolveLifetimeManager (Nuevo en Unity 2.0): Asegura que las instancias de un

tipo de objeto se reutilizan a lo largo de todo un grafo de dependencias de objetos

creados por Unity.

o ContainerControlledLifetimeManager: Implementa un comportamiento ‘singleton’ para

cada objeto creado. El contenedor si mantiene referencias a los objetos creados y se

hace un dispose automático cuando se hace un dispose del contenedor.

o ExternallyControlledLifetimeManager: Este LifeTimeManager mantiene una referencia

débil a su instancia manejada. Es decir, implementa un comportamiento ‘singleton’ pero

el contenedor no mantiene una referencia al objeto que deberá ser eliminado

(disposed) cuando se salga del ámbito.

o PerThreadLifetimeManager: Reutiliza una única instancia de cada clase por thread que

accede al grafo de objetos de dependencias creado por Unity.

Interfaz por el que se ‘preguntará’ Clase de Implementación asociada

Tipo de ‘LifeTimeManager’

Page 6: LAB 02 Lab Implementando Desacoplamiento IoC DI Con UNITY

Page | 6

o HierarchicalifetimeManager (Nuevo en Unity 2.0): Implementa un comportamiento

singleton, pero los contenedores hijos no comparten instancias con los contenedores

padres.

o PerExecutionContextLifetimeManager (Custom de NLayerSampleApp): Este

LifeTimeManager es ‘custom’ creado en la aplicación ejemplo NLayerApp. Su

comportamiento es parecido a ‘PerResolveLifetimeManager’, pero en lugar de ser

compartidas las instancias entre el grafo de objetos de dependencias a partir del primer

resolve(), cuando hacemos uso de ‘PerExecutionContextLifetimeManager’, los objetos

se comparten por todo el contexto de ejecución inicial, por ejemplo, para todas las

operaciones de una petición WCF (a partir de un WebMethod) o a partir de una petición

ASP.NET o incluso a partir de una ejecución de un método de Pruebas Unitarias.

En concreto nos basamos en los siguientes contextos:

OperationContext de WCF

HttpContext de ASP.NET

CallContext para UnitTesting, WinForms, WPF etc.

5. Como alterativa, podemos realizar el registro de tipos de una forma más desacoplada,

mediante configuración XML. Analizar el siguiente XML:

(NOTA: El nombre de los tipos no coincide pues este XML procede de una versión antigua de

NLayerSampleApp).

<?xml version="1.0" encoding="utf-8" ?> <unity> <typeAliases> <!-- Lifetime manager types --> <typeAlias alias="singleton" type="Microsoft.Practices.Unity.ContainerControlledLifetimeManager, Microsoft.Practices.Unity" /> <typeAlias alias="perThread" type="Microsoft.Practices.Unity.PerThreadLifetimeManager, Microsoft.Practices.Unity" /> <typeAlias alias="external" type="Microsoft.Practices.Unity.ExternallyControlledLifetimeManager, Microsoft.Practices.Unity" /> <typeAlias alias="perCall" type="Microsoft.Practices.Unity.TransientLifetimeManager, Microsoft.Practices.Unity"/> <!-- EF Context & Fake--> <typeAlias alias="IMainModuleContext" type="Microsoft.Samples.NLayerApp.Infrastructure.Data.MainModule.Context.IMainModuleContext, Microsoft.Samples.NLayerApp.Infrastructure.Data.MainModule " /> <typeAlias alias="MainModuleContext" type="Microsoft.Samples.NLayerApp.Infrastructure.Data.MainModule.Context.MainModuleContext, Microsoft.Samples.NLayerApp.Infrastructure.Data.MainModule" /> <typeAlias alias="MainModuleFakeContext" type="Microsoft.Samples.NLayerApp.Infrastructure.Data.MainModule.Mock.MainModuleFakeContext, Microsoft.Samples.NLayerApp.Infrastructure.Data.MainModule.Mock" /> <!-- Repositories--> ... ...

Page 7: LAB 02 Lab Implementando Desacoplamiento IoC DI Con UNITY

Page | 7

<typeAlias alias="ICustomerRepository" type="Microsoft.Samples.NLayerApp.Domain.MainModule.Contracts.ICustomerRepository, Microsoft.Samples.NLayerApp.Domain.MainModule" /> <typeAlias alias="CustomerRepository" type="Microsoft.Samples.NLayerApp.Infrastructure.Data.MainModule.Repositories.CustomerRepository, Microsoft.Samples.NLayerApp.Infrastructure.Data.MainModule" /> ... ... <!--Domain Services--> <typeAlias alias="IBankTransfersService" type="Microsoft.Samples.NLayerApp.Domain.MainModule.Services.IBankTransfersService, Microsoft.Samples.NLayerApp.Domain.MainModule" /> <typeAlias alias="BankTransfersService" type="Microsoft.Samples.NLayerApp.Domain.MainModule.Services.BankTransfersService, Microsoft.Samples.NLayerApp.Domain.MainModule" /> ... ... </typeAliases> <!-- UNITY CONTAINERS --> <containers> <container name="RootContext"> <types> <!-- MAPPING from Interfaces to Classes --> <type type="ICustomerRepository" mapTo="CustomerRepository"> <lifetime type="perCall"/> </type> ... ... <type type="IBankTransfersService" mapTo="BankTransfersService"> <lifetime type="perCall"/> </type> ... ... </types> </container> <container name="RealAppContext"> <types> <type type="IMainModuleContext" mapTo="MainModuleContext"> <lifetime type="perCall" /> </type> </types> </container> <container name="FakeAppContext"> <types> <type type="IMainModuleContext" mapTo="MainModuleFakeContext"> <lifetime type="perCall" /> </type> </types> </container> </containers> </unity>

Sin embargo, en tiempo de desarrollo puede ser más farragoso, pues si nos confundimos en

la escritura de tipos dentro del XML, los errores los obtendremos en tiempo de ejecución en

lugar de en tiempo de compilación.

6. En nuestro caso (NLAYERAPP), para abstraernos de la implementación específica de UNITY,

estamos utilizando un Interfaz nuestro llamado ‘IContainer’. De esta forma, al estar

trabajando mediante abstracciones con el propio contenedor IoC, podríamos llegar a

sustituir en el futuro a UNITY por otro contenedor IoC, de una forma más sencilla.

Analizar el interfaz IContainer dentro del fichero IContainer.cs.

Page 8: LAB 02 Lab Implementando Desacoplamiento IoC DI Con UNITY

Page | 8

Page 9: LAB 02 Lab Implementando Desacoplamiento IoC DI Con UNITY

Page | 9

Paso 2 – Haciendo uso de Jerarquía de Contenedores UNITY para cambio dinámico a uso de objetos

‘Fake’.

1. Registro de tipos en jerarquía de varios contenedores: Para poder hacer mocking de forma

dinámica como vimos en los Labs anterioes cambiando simplemente una clave de

configuración, en NLayerSampleApp hacemos uso de diferentes contenedores de Unity, con

la siguiente jerarquía:

Ver en el fichero IoCUnityContainers que en el contenedor ‘RealAppContext’ tenemos

registrado los tipos del contexto real de Entity Framework y en el contenedor

‘FakeAppContext’ registramos los tipos que hacen mocking del contexto de EF. Lógicamente

en ambos contenedores, los interfaces son los mismos, pues cuando se pide un objeto para

un interfaz dado, dicho interfaz estará mapeado de forma diferente en cada uno de los dos

contenedores Unity:

Mismo Interfaz por el que se ‘preguntará’

Mismo Interfaz por el que se ‘preguntará’

Mapeo a clase de UoW de Entity Fraework

Mapeo a clase Fake de UoW

Page 10: LAB 02 Lab Implementando Desacoplamiento IoC DI Con UNITY

Page | 10

De esta forma, cuando la propiedad de configuración AppSettings["defaultIoCContainer"]

es ‘RealAppContext’, haremos uso del contenedor RealAppContext añadiendo/solapando sus

tipos al de RootContainer. Y el mismo efecto cuando la propiedad es ‘RealAppContext’, pero

entonces solaparemos los tipos del contenedor ‘FakeAppContainer’ .

Recordamos la propiedad de configuración utilizada en el LAB 01:

2. La implementación de los UoW y contextos de EF ó Fake, se analizarán en los Labs de la Capa

de Infraestructura de Persistencia y Acceso a datos.

OTRAS POSIBILIDADES DEL SISTEMA DINAMICO DE INYECCION DE DEPENDENCIAS

Hay que tener en cuenta que este sistema dinámico de instanciación de unos u otros objetos como

implementación de abstracciones (interfaces), puede ser útil no solo para hacer mocking de Entity

Framework. Puede utilizarse para otros sistemas de mocking, por ejemplo de acceso o no a un sistema

backend o legacy (ERP, etc.) o bien a un fake que simule dicho acceso, etc. Las posibilidades están ahora

abiertas a la imaginación.

Page 11: LAB 02 Lab Implementando Desacoplamiento IoC DI Con UNITY

Page | 11

Ejercicio 2: Inyeccio n de Dependencias

con Unity en la Aplicacio n ejemplo de la

Arquitectura

La inyección de dependencias que hace uso NLayerSampleApp es mayoritariamente basada en

constructores, es decir, especificando las dependencias de los Servicios en los constructores.

Paso 1 – Analizar el Servicio ‘BankingManagementService’ de Application Layer.

1. Abrir el fichero ‘BankingManagementService.cs’ del proyecto Application.MainModule:

public class BankingManagementService : IBankingManagementService { IBankTransferDomainService _bankTransferDomainService; IBankAccountRepository _bankAccountRepository; public BankingManagementService(IBankTransferDomainService bankTransferDomainService, IBankAccountRepository bankAccountRepository) { ... _bankTransferDomainService = bankTransferDomainService; _bankAccountRepository = bankAccountRepository; } public void PerformTransfer(string fromAccountNumber, string toAccountNumber, decimal amount) { //Process: 1º Start Transaction // 2º Get Accounts objects from Repositories // 3º Call PerformTransfer method in Domain Service // 4º If no exceptions, save changes using repositories and Commit Transaction //Create a transaction context for this operation TransactionOptions txSettings = new TransactionOptions() { Timeout = TransactionManager.DefaultTimeout, IsolationLevel = IsolationLevel.Serializable // review this option }; using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, txSettings)) { //Get Unit of Work IUnitOfWork unitOfWork = _bankAccountRepository.UnitOfWork as IUnitOfWork; //Create Queries' Specifications BankAccountNumberSpecification originalAccountQuerySpec = new BankAccountNumberSpecification(fromAccountNumber); BankAccountNumberSpecification destinationAccountQuerySpec = new BankAccountNumberSpecification(toAccountNumber); //Query Repositories to get accounts BankAccount originAccount = _bankAccountRepository.GetBySpec(originalAccountQuerySpec as ISpecification<BankAccount>)

Dependencias especificadas en Constructor, un Repositorio y un Servicio de Dominio

Page 12: LAB 02 Lab Implementando Desacoplamiento IoC DI Con UNITY

Page | 12

.SingleOrDefault(); BankAccount destinationAccount = _bankAccountRepository.GetBySpec(destinationAccountQuerySpec as ISpecification<BankAccount>) .SingleOrDefault(); if (originAccount == null || destinationAccount == null) throw new InvalidOperationException(Resources.Messages.exception_InvalidAccountsForTransfer); ////Start tracking STE entities (Self Tracking Entities) originAccount.StartTrackingAll(); destinationAccount.StartTrackingAll(); //Excute Domain Logic for the Transfer (In Domain Service) _bankTransferDomainService.PerformTransfer(originAccount, destinationAccount, amount); //Save changes and commit operations. //This opeation is problematic with concurrency. //"balance" property in bankAccount is configured //to FIXED in "WHERE concurrency checked predicates" _bankAccountRepository.Modify(originAccount); _bankAccountRepository.Modify(destinationAccount); //Complete changes in this Unit of Work unitOfWork.CommitAndRefreshChanges(); //Commit the transaction scope.Complete(); } }

2. Es importante destacar que, como se puede observar, no hemos hecho ningún ‘new’ explícito de

clases de Ropositorios (como BankAccountRepository) o de Servicios. Es el contenedor de Unity el que

automáticamente creará el objeto de BankAccountRepository y BankTransferDomainService

proporcionándolos como parámetro de entrada a nuestro constructor. Esa es precisamente la inyección

de dependencias en el constructor.

Paso 2 – Inicio de resolución de tipos y creación de grafo de objetos.

En tiempo de ejecución, el código de instanciación de BankingManagementService se realizás utilizando

el método Resolve() del contenedor de Unity, el cual origina la instanciación generada por el framework

de Unity de la clase BankAccountRepository dentro del ámbito de la clase

BankingManagementService.

1. Abrir el fichero MainModuleService.BankingManagement.cs del proyecto WCF de Servicios

Distribuidos (DistributedServices.MainModule) desde donde se arranca la creación de grafos de objetos

creados por Unity.

Este tipo de código es el que implementaremos normalmente en la capa de primer nivel que consume

objetos de Capa de Aplicación y del Dominio, es decir, normalmente en la capa de Servicios Distribuidos

(WCF) como este caso o incluso capa de presentación web ejecutándose en el mismo servidor de

aplicaciones (ASP.NET):

Page 13: LAB 02 Lab Implementando Desacoplamiento IoC DI Con UNITY

Page | 13

public partial class MainModuleService //Parte de Servicio WCF { ... ... public void PerformBankTransfer(TransferInformation transferInformation) { try { IBankingManagementService bankingManagement = IoCFactory.Instance.CurrentContainer.Resolve<IBankingManagementService>(); bankingManagement.PerformTransfer(transferInformation.OriginAccountNumber, transferInformation.DestinationAccountNumber, transferInformation.Amount); } catch(…) ... } }

Como se puede observar en el uso de Resolve<>(), en ningún momento hemos creado nosotros una

instancia de las clases de los tipos que dependemos (IBankTransferDomainService y

IBankAccountRepository) y por lo tanto nosotros no hemos pasado explícitamente dichos objetos al

constructor de nuestra clase BankingManagementService. Y sin embargo, cuando se instancie la clase

de servicio (BankingManagementService), automáticamente se nos habrá proporcionado en el

constructor las instancias de las dependencias (IBankTransferDomainService y

IBankAccountRepository). Eso lo habrá hecho precisamente el contenedor de Unity al detectar la

dependencia. Esta es la inyección de dependencia y nos proporciona la flexibilidad de poder cambiar la

dependencia en tiempo de configuración y/o ejecución. Por ejemplo, si en el fichero XML de

configuración hemos especificado que se creen objetos Mock (simulación) en lugar de objetos reales de

acceso a datos (Repository), la instanciación de las clases de implementación sería diferente.

2. Observar las definiciones internas de interfaces y clases que se utilizan en el código anterior, como la

definición de la clase de Servicio de AppLayer ‘BankingManagementService’ y el resto de dependencias en

su grafo.

Interfaz de Servicio de Application Layer, a resolver. A partir de aquí se crea el grafo de objetos

Page 14: LAB 02 Lab Implementando Desacoplamiento IoC DI Con UNITY

Page | 14

Ejercicio 3 (OPCIONAL): Inyeccio n de

Factorí as (Injection-Factory)

La inyección de factorías es una característica nueva en UNITY 2.0. En la versión actual de

NLayerSampleApp no lo estamos utilizando porque no lo hemos necesitado, pero puede ser algo muy

útil cuando la creación de un objeto debe ser realizada por una Factory que debe tener en cuenta

aspectos externos a dicho objeto y no nos es suficiente con apoyarnos solamente en el constructor.

Así pues, InjectionFactory es un mecanismo que permite indicarle a Unity un método (una

Func<IUnityContainer, object>, usualmente una lambda expresion) a usar cada vez que deba resolver un

objeto especificado. Como decíamos antes, permite que Unity use nuestras propias factorías.

Si tenemos la siguiente factoría para crear objetos del tipo IComplexService:

interface IComplexServiceFactory { IComplexService GetNewInstance(); } class ComplexServiceFactory : IComplexServiceFactory { public IComplexService GetNewInstance() { IComplexService complexService = new ComplexService(); // Do required actions by our factory... complexService.Initialize("dato 3", "dato 4", "dato 5"); return new complexService(); } }

A la hora de hacer uso con UNITY para resolver el tipo y crear el grafo de objetos, primero tenemos que

realizar el siguiente registro:

//Register Complex-Service using a Factory container.RegisterType<IComplexServiceFactory, ComplexServiceFactory>(new ContainerControlledLifetimeManager()); container.RegisterType<IComplexService>(new InjectionFactory(x => x.Resolve<IComplexServiceFactory>().GetNewInstance()));

Page 15: LAB 02 Lab Implementando Desacoplamiento IoC DI Con UNITY

Page | 15

En la última línea le indicamos a Unity que cada vez que alguien haga un Resolve<IComplexService>

ejecute el delegate que le indicamos, en este caso que obtenga una IComplexServiceFactory y llame al

método GetNewInstance().

El Resolve() se haría de forma normal, pues el uso de la factoría es interno:

IComplexService complexService = IoCFactory.Instance.CurrentContainer.Resolve<IComplexService>();

NOTA AL MARGEN:

En el caso de necesitar parámetros de entrada en el constructor de nuestra clase a resolver, esto

también se puede realizar con UNITY 2.0, de forma similar a la siguiente:

IComplexService complexService = IoCFactory.Instance.CurrentContainer.Resolve<IComplexService>( new ParameterOverride("param1", 3));

Page 16: LAB 02 Lab Implementando Desacoplamiento IoC DI Con UNITY

Page | 16

Ejercicio 4 (OPCIONAL): AOP e

intercepcio n de llamadas con UNITY

Uno de los usos fundamentales de AOP (Aspect Oriented Programming) consiste en eliminar

‘aparentemente’ código explícito utilizado para implementar aspectos 'Cross-Cutting'. Estos aspectos

normalmente suelen ser cosas como Logging, Validaciones, Gestión de Excepciones, etc. Esto se realiza

extrayendo dichos aspectos a un punto central reutilizable por todas las capas, de forma que los

desarrolladores puedan aplicarlo de una forma más transparente (aplicando aspectos o 'marcas' en

forma de atributos). De esta forma, el desarrollador puede focalizar más y de una forma mas clara en la

lógica del Dominio y de su aplicación concreta.

Hay diferentes formas de ‘inyectar’ estas acciones de una forma transparente. Es lo que en AOP se

denomina Aspect Waiver. Una forma puede ser generando código (C# o incluso IL) en tiempo de

compilación. Otra forma puede ser realizando intercepción de llamadas entre objetos e inyectando

ejecución de código entre dichas intercepciones de llamadas.

El mecanismo de intercepción captura la llamada realizada a un objeto (en tiempo de ejecución) y

proporciona la implementación completa de dicho objeto. Unity utiliza la clase 'Interceptor' para

especificar el mecanismo de intercepción a utilizar y como ocurre la intercepción. Por otro lado, utiliza la

clase InterceptionBehavior para describir qué hacer cuando un objeto se ha interceptado.

La intercepción de Unity está diseñada para ejecutar sus comportamientos sobre todo el objeto

completo y todos sus métodos.

Los Interceptadores juegan un rol solo en tiempo de creación del proxy (o tipo derivado). Una vez que el

proxy o tipo derivado se ha creado, el interceptador habrá proporcionado todos los componentes

requeridos por el objeto interceptado y lo habrá incorporado al procesamiento del proxy.

Page 17: LAB 02 Lab Implementando Desacoplamiento IoC DI Con UNITY

Page | 17

NOTA: Unity proporciona intercepción de llamadas a nivel de instancia y de tipo. Además esta

intercepción de llamadas puede realizarse con objetos contenidos en el contenedor IoC de Unity o bien

haciendo uso del API 'standalone' de UNITY (static intercept class) sin estar haciendo uso del contenedor

DI/IoC de Unity.

En el caso de hacer uso de Intercepción de llamadas cuando utilizamos el contenedor IoC de Unity,

deberemos seguir los siguientes pasos:

3. Añadir referencia al assembly de intercepción en UNITY:

4. Después de la creación del contenedor a utilizar, deberemos extenderlo para que soporte la

intercepción de llamadas. Así por ejemplo, dentro del constructor de nuestro

IoCUnityContainer, extenderíamos el código de la siguiente forma:

//Add this ‘using’

using Microsoft.Practices.Unity.InterceptionExtension;

...

...

public IoCUnityContainer()

{

...

//Create root container

IUnityContainer rootContainer = new UnityContainer();

//Configure container to support Interception

rootContainer.AddNewExtension<Interception>();

...

}

Page 18: LAB 02 Lab Implementando Desacoplamiento IoC DI Con UNITY

Page | 18

5. Configurar al contenedor de forma que realice intercepción cuando resuelva la clase donde

queremos realizar intercepción de llamadas. En este caso, por ejemplo, para nuestra clase

del Servicio del dominio 'BankTransferDomainService'.

Para ello, debemos indicar el mecanismo de intercepción a utilizar con el objeto Interceptor

y el comportamiento de intercepción a utilizar con un objeto InterceptionBehavior. Modificar

nuestro código, en el fichero ‘IoCUnityContainer.cs’ (donde registramos los tipos y

especificamos los mapeos), de la siguiente forma:

//Register domain services mappings

//(CDLTLL) Extending with Calls Interception

container.RegisterType<IBankTransferDomainService, BankTransferDomainService>(

new TransientLifetimeManager(),

new Interceptor<InterfaceInterceptor>(),

new InterceptionBehavior(new AuditBehavior(new

TraceSource("interception-audit-source"))));

6. Lógicamente, para que este código nos funcione, primero deberemos de haber definido

tanto en TraceSource "interception-audit" en el Web.config, como nuestra clase de ‘custom

behavior’ que hemos llamado ‘AuditBehavior’.

7. En el Web.config añadiríamos lo siguiente, para definir la fuente a utilizar:

<sources> ...

<source name="interception-audit-source" switchValue="All"> <listeners> <add name="interception-audit-listener"/> </listeners> </source> </sources> ...

...

<sharedListeners> ... ... <add name="interception-audit-listener" initializeData="interception-audit.log" traceOutputOptions="DateTime" type="System.Diagnostics.TextWriterTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/> </sharedListeners>

8. Y añadiríamos la siguiente clase de Behavior al proyecto de UNITY (Puesto que un Behaviour

es exclusivamente para Intercepción de llamadas en UNITY):

Page 19: LAB 02 Lab Implementando Desacoplamiento IoC DI Con UNITY

Page | 19

using System; using System.Collections.Generic; using System.Diagnostics; using Microsoft.Practices.Unity.InterceptionExtension; namespace Microsoft.Samples.NLayerApp.Infrastructure.CrossCutting.IoC.Unity { class AuditBehavior : IInterceptionBehavior, IDisposable { private TraceSource source; public AuditBehavior(TraceSource source) { if (source == null) throw new ArgumentNullException("source"); this.source = source; } public IEnumerable<Type> GetRequiredInterfaces() { return Type.EmptyTypes; } public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) { this.source.TraceInformation( "Invoking {0}", input.MethodBase.ToString()); IMethodReturn methodReturn = getNext().Invoke(input, getNext); if (methodReturn.Exception == null) { this.source.TraceInformation( "Successfully finished {0}", input.MethodBase.ToString()); } else { this.source.TraceInformation( "Finished {0} with exception {1}: {2}", input.MethodBase.ToString(), methodReturn.Exception.GetType().Name, methodReturn.Exception.Message); } this.source.Flush(); return methodReturn; } public bool WillExecute {

Page 20: LAB 02 Lab Implementando Desacoplamiento IoC DI Con UNITY

Page | 20

get { return true; } } public void Dispose() { this.source.Close(); } } }

8. También podríamos implementar todo este sistema de Intercepción de llamadas mediante

XML, extendiendo el XML de configuración de UNITY. Sin embargo, pasa lo mismo que

advertimos anteriormente. Si se comente un error tipográfico, el error se detectará en

tiempo de ejecución en lugar de en tiempo de compilación, lo cual es mucho peor. En

versiones finalizadas de la aplicación, si es una opción viable.

9. También se puede hacer uso de atributos e inyección de POLICIES para aspectos más

avanzados de intercepción de llamadas.

Por ejemplo, en el método del Dominio ‘’ de la clase ‘’, podríamos aplicar un atributo

diciendo que para ese método aplique la auditoría, y queiens no tengan dicho atributo, no

apliquen la auditoría (o la lógica que más conveniente se vea). Por ejemplo:

public class BankTransferDomainService : IBankTransferDomainService { [SetAuditSystem("interception-audit-source")] public void PerformTransfer(BankAccount originAccount, BankAccount destinationAccount, decimal amount) {

...

...

Este sistema de atributos y políticas no explicamos aquí como se implementaría y lo dejamos

a la curiosidad del lector estudiando los siguientes enlaces de Atributos y Políticas de UNITY:

http://207.46.16.251/en-us/library/ff660860(PandP.20).aspx

http://207.46.16.248/en-us/library/ff660915(PandP.20).aspx

http://blog.samstephens.co.nz/2010-11-15/policy-injection-attributes-preempt-calls-

functioning-systems/

Page 21: LAB 02 Lab Implementando Desacoplamiento IoC DI Con UNITY

Page | 21

APENDICE 1: Unity vs. MEF

MEF (Microsoft Extensibility Framework) no es un ‘Full IoC container’, aun cuando utiliza

conceptos IoC. Es un rfamework orientado a dar extensibilidad.

MEF focaliza en extensibilidad de aplicaciones con ‘descubrimiento de componentes’ y

‘composición’.

Unity si es un ‘IoC container’ tradicional, es su principal propósito.

Hasta la fecha (Febrero 2011), el posicionamiento de ambas tecnologías es el anterior. Y la

recomendación para realizar Inyección de Dependencias entre componentes de una Arquitectura N -

Layer es más recomendable hacerlo con Unity que con MEF. Unity es más potente que MEF en muchos

aspectos IoC MEF, y por el contrario, MEF está más indicado para extensibilidad de aplicaciones

(especialmente en capa de presentación) a realizar incluso por terceras partes que no son quienes han

creado la aplicación.

En futuras versiones de MEF si es posible que vaya realizando un crecimiento y ‘absorción’ de

capacidades de Unity. Las capacidades de MEF serán mucho mayores en el futuro. Pero esto es un

futurible, la realidad actual es lo especificado en los anteriores puntos.

Blog Posts interesantes al respecto:

http://gorskib.wordpress.com/2010/12/04/is-mef-a-next-dependency-injection-container-%E2%80%93-

my-vote-no/

http://stackoverflow.com/questions/293051/is-mef-a-dependency-injection-framework