Mostrando las entradas con la etiqueta Castle Windsor. Mostrar todas las entradas
Mostrando las entradas con la etiqueta Castle Windsor. Mostrar todas las entradas

Castle.Windsor Fluent Registration API

Ya vimos en un post anterior como podemos configurar el container de Castle con Fluent Registration API al igual que con xml. Ahora vamos a ver algunas cosas que podemos hacer con Fluent donde, quizás, empiece a sacar ventaja. Espero que el siguiente código sea suficientemente claro.

Creamos nuestro container

IWindsorContainer container = new WindsorContainer();
Registramos todos los DAOs
container.Register(
AllTypes.Of<IDAO>()
.From(typeof(InvoiceDAO).Assembly.GetTypes())
.WithService.FromInterface()
.Configure(cr => cr.LifeStyle.Singleton));

“registramos en el container” (línea 1), “todos los types que implementan IDAO” (línea 2), “del assembly al que pertenece InvoiceDAO (en este caso DMS.DAOs.NH” (línea 3), “como servicio tomamos la interface que implementan” (línea 4) y “los configuramos como singleton” (línea 5).

Registramos todos los models
container.Register(
AllTypes.Of<IModel>()
.From(typeof (InvoiceViewModel).Assembly.GetTypes())
.WithService.FromInterface());

creo que ya no hace falta “leerles” lo que dice el código. La diferencia con la registración de los DAOs es que en este caso registramos todas las implementaciones de IModel en el assembly DMS.Models.Impl.

Registramos todos los services
container.Register(
AllTypes.Of<IService>()
.From(typeof(InvoiceService).Assembly.GetTypes())
.WithService.FromInterface()
.Configure(cr => cr.Interceptors<SessionInterceptor>()));
Igual que antes, salvo por que les configuramos el interceptor (SessionInterceptor).

Conclusión

Por si no quedó claro, la ventaja que intento mostrarles es que no necesitamos declarar cada componente (existente o que existirá), sino que con esta configuración y si seguimos implementando las interfaces correspondientes (IDAO, IModel e IService) automáticamente se agregarán al container y se inyectaran las dependencias.

Conectando las piezas

Como dijimos en un post anterior, para lograr un bajo acoplamiento hacemos referencia a contratos (interfaces) y no a implementaciones (clases), pero cuando queremos ejecutar nuestra aplicación (o parte de ella) necesitamos hacer uso de las implementaciones. Y qué mejor que delegar el control de creación de objetos en algún framework especializado para tal fin. ver Inversion of Control (IoC) y Dependency Injection (DI)

En nuestro ejemplo usamos Castle Windsor, pero podría ser Sprint.NET, Unity, LinFu, etc. y hasta sería interesante abstraernos de quien sea nuestro IoC / DI, y para esto contamos con Common Service Locator.


La idea de todo esto es poder solicitar un determinado objeto, por ejemplo un Model, y que ya tenga sus referencias a otros objetos inyectadas, como por ejemplo los DAOs que necesita.

¿Cómo hacemos este "cableado" con Castle?

 ver documentación completa

Para declarar un componente:

<component id="IInvoiceDAO"
           service="DMS.DAOs.IInvoiceDAO, DMS.DAOs"
           type="DMS.DAOs.NH.InvoiceDAO, DMS.DAOs.NH"
           lifestyle="singleton">
</component>

donde id es la identificación del componente tanto para referencias internas en el archivo de configuración como para solicitarlo desde nuestra aplicación, service es el contrato que debe cumplir y type es la implementación que deseamos usar.

y para interconectar componentes: 

        <component id="IInvoiceViewModel"
                       service="DMS.Models.IInvoiceViewModel, DMS.Models"
                       type="DMS.Models.Impl.InvoiceViewModel, DMS.Models.Impl">
            <
parameters>
                <
InvoiceDAO>${IInvoiceDAO}</InvoiceDAO>
            </
parameters>
        </
component>

donde parameters son la lista de parámetros del constructor de InvoiceViewModel o propiedades púbicas de este. En este caso estamos inyectando el componente definido como IInvoiceDAO.

Nota: si tenemos un único componente del tipo del parámetro, Castle lo va a inyectar sin necesidad de que se lo especifiquemos. es decir, que en este ejemplo no sería necesario indicar el parámetro.

Otra forma de configurar el container

También podemos configurar Castle Windsor mediante Fluent Registration API de la siguiente manera:

IWindsorContainer container = new WindsorContainer()
.AddFacility("factory.support", new FactorySupportFacility())
.Register(Component.For<MockHelper>()
.ImplementedBy<MockHelper>())
.Register(Component.For<IInvoiceDAO>()
.UsingFactoryMethod(kernel => kernel.Resolve<MockHelper>().CreateInvoiceDAO())
.LifeStyle.Singleton)
.Register(Component.For<IInvoiceViewModel>()
.ImplementedBy<InvoiceViewModel>()
.LifeStyle.Transient);

Como podemos ver, en esta configuración no estamos especificando los parámetros, como dijimos antes en Nota, no es necesario especificarlo en este caso.

Usando CommonServiceLocator

Finalmente, para abstraernos de la implementación del IoC container, podemos crear un ServiceLocator pasándole como parámetro el container ya configurado:

IServiceLocator serviceLocator = new WindsorServiceLocator(container);

y luego nos referiremos a este de la siguiente forma:

IInvoiceViewModel model = serviceLocator.GetInstance<IInvoiceViewModel>();

Descargar el código fuente completo