El pasado 4 de Abril del 2008, fue liberada la versión de producción del proyecto Unity. Este es un IoC Container o contenedor de servicios, que nos permitirá a las aplicaciones existentes desacoplarlas con gran facilidad o las nuevas crearlas desacopladas. De hecho ya esta Michael Puleilo trabajando en hacer una nueva versión de WCSF con Unity (Ver en: http://blogs.msdn.com/mpuleio/archive/2008/04/04/converting-the-composite-web-application-block-to-unity-intro.aspx ).
Una de las ventajas de Unity es que es un framework liviano que aprendiendo un par de métodos del framework estamos listos para desarrollar soluciones desacopladas. En este primer ejemplo vamos a ver como Unity nos facilita el trabajo versus realizar el desacoplamiento aplicando el patrón de Dependency Injection a pie.
Veamos el siguiente código escrito en C# 3.0:
Tenemos una entidad de negocio llamada cliente:
namespace MyFirstUnityApp.ServicioClienteFld
{
public class Cliente
{
public int? CodigoCliente
{
get;
set;
}
public string RazonSocial
{
get;
set;
}
public string Direccion
{
get;
set;
}
}
}
Y tenemos una intefase que definie un metodo que devuelve un listado de dicha entidad:
using System.Collections.Generic;
namespace MyFirstUnityApp.ServicioClienteFld
{
public interface IClienteServicio
{
IList<Cliente> ListarTodo();
}
}
Ahora una clase que implemente esta interface:
using System.Collections.Generic;
namespace MyFirstUnityApp.ServicioClienteFld
{
public class ClienteServicio : IClienteServicio
{
public IList<Cliente> ListarTodo()
{
return new List<Cliente> {new Cliente
{
CodigoCliente = 1, RazonSocial = "Cliente la estrella", Direccion = "zona 10"
} ,
new Cliente{
CodigoCliente = 2, RazonSocial = "Ingenio", Direccion = "zona 15"
}
};
}
}
}
Recuerda estamos en C# 3.0 podemos crear el contenido del arreglo en la declaración de una vez. Usualmente aquí hacemos una llamada a la capa de datos, para obtener la información de algún proveedor de datos, pero no es de importancia ahora.
Luego nuestra clase que consumirá nuestro servicio:
namespace MyFirstUnityApp.ServicioClienteFld
{
public class MostrarListadoClientes
{
public MostrarListadoClientes() { }
public MostrarListadoClientes(IClienteServicio clienteServicioInyectado)
{
_clienteServicio = clienteServicioInyectado;
}
public IClienteServicio ClienteServicio { get; set; }
public IList<Cliente> ListadoClientes()
{
return ClienteServicio.ListarTodo();
}
public IList<Cliente> ListadoClientesV2()
{
return _clienteServicio.ListarTodo();
}
}
}
Para este ejemplo definimos tanto un constructor o una propiedad de la clase donde le inyectaremos el servicio. Esto al momento de hacer en un aplicación real se decide que camino tomar.
Ahora veamos como se consumen los servicios manualmente en las clases que dependen de ellas.
En el primer ejemplo veremos como inyectamos el servicio en el constructor:
MostrarListadoClientes mostrarClientes =
new MostrarListadoClientes(new ClienteServicio());
IList<Cliente> ListadoInstanciaPorConstructor = mostrarClientes.ListadoClientesV2();
ImprimirListado(ListadoInstanciaPorConstructor);
Dentro del constructor MostrarListadoClientes estamos pasmándole la clase que implementa la interface IClienteServicio, como se muestra en la imagen abajo.
Ahora veamos como la inyectaríamos el servicio en la propiedad:
MostrarListadoClientes mostrarClientesPorPropiedad = new MostrarListadoClientes();
mostrarClientesPorPropiedad.ClienteServicio = new ClienteServicio();
ImprimirListado(mostrarClientesPorPropiedad.ListadoClientes());
Ahora esto no es nada complicado, pero imaginese que la clase que implementa IClienteServicio tiene otro servicio que de pende esta misma y esta a otra y así sucesivamente esto tiende a complicarse y hacer engorroso la escritura del código. Veamos el código de lo que estoy planteando:
IContectividadServicio NuevaConectividadServicio = new ConectividadServicio();
IPermisoServicio NuevoPermisoSerivcio = new PermisoServicio(NuevaConectividadServicio);
IClienteServicio NuevoClienteSerivico = new ClienteServicio(PermisosServicio);
MostrarListadoClientes mostrarClientes =
new MostrarListadoClientes(new ClienteServicio());
IList<Cliente> ListadoInstanciaPorConstructor = mostrarClientes.ListadoClientesV2();
ImprimirListado(ListadoInstanciaPorConstructor);
Aquí es donde entra Unity en acción el código anterior quedaría en Unity de la siguiente forma:
Primero registramos los servicios disponibles en nuestra aplicación, de la siguiente forma:
IUnityContainer container = new UnityContainer()
.RegisterType<IClienteServicio, ClienteServicio>()
.RegisterType<IPermisoServicio, PermisoServicio>()
.RegisterType<IConectividadServicio, ConectividadServicio> ();
Nos pegamos a la interfase que define el contrato del contenedor IoC y luego le pasamos la instancia de UnityContiner y luego registramos todos los servicios, primero la interfase y luego la clase que implementa esta interfase. Ahora consumamos la clase de la cual depende todos estos servicios y obtengamos el listado del cliente:
IList<Cliente> ListadoInstanciaPorPropiedad = container.Resolve<MostrarListadoClientes>().ListadoClientes();
Resolve se utiliza para resolver todas las dependencias de la clase no necesitamos indicarle el momento que debe de inyectarlo, ni siguiera crear en memoria la clase dependiente el lo hace por nosotros. Si es importante el orden de registrar los servicios debemos empezar del servicio padre o base al servicio hijo o dependiente.
Solo necesitamos etiquetar con un atributo de dependencia en la clase consumidora o dependiente a si como muestro abajo en el código en cada una de las clases. En el orden de dependencia se los muestro a continuación:
public class MostrarListadoClientes
{
public MostrarListadoClientes() { }
[Dependency]
public IClienteServicio _clienteServicio { get; set; }
public IList<Cliente> ListadoClientes()
{
return _clienteServicio.ListarTodo();
}
public class ClienteServicio : IClienteServicio
{
[Dependency]
public IPermisoServicio PermisoServicio
{
get;
set;
}
#region IClienteServicio Members
public IList<Cliente> ListarTodo()
{
if (PermisoServicio.IsInRole("usuario", "ListarClientes"))
{
return new List<Cliente>
{
new Cliente
{
CodigoCliente = 1,
RazonSocial = "Cliente la estrella",
Direccion = "zona 10"
}
,
new Cliente
{
CodigoCliente = 2,
RazonSocial = "Ingenio",
Direccion = "zona 15"
}
}
;
}
else
return null;
}
#endregion
}
public class PermisoServicio : IPermisoServicio
{
[Dependency]
public IConectividadServicio ConectividadServicio
{
get;
set;
}
#region IPermisoServicio Members
public bool IsInRole(string usuario, string opcion)
{
if (ConectividadServicio.EstablecerConectividad())
return true;
else
return false;
}
#endregion
}
public class ConectividadServicio: IConectividadServicio
{
#region IConectividadServicio Members
public bool EstablecerConectividad()
{
return true;
}
#endregion
}
Bueno y esto es solo una muestra sencilla de todo lo que podemos hacer en Unity... En otro artículo vamos a ver como obtenerlo de un archivo de configuración y las distintas alternativas que tenemos en Unity.. además veremos como se implementa el patrón MVP con Unity en un aplicación Windows. Hasta la próxima Code4Fun !.
Su amigo,
Manolo Herrera