11.23.2010

Utilizando Unity para Resolver un Problema de Negocio - 2

En el post anterior presentamos un problema el cual resolvimos usando Unity, sin embargo, este problema se presentó de una manera muy simple y un poco corto en realidad. En este post vamos a agregarle un par de situaciones que se presentan normalmente en este tipo de sistemas.

En primer lugar, cuando creamos la instancia del usuario que necesitamos, le tuvimos que indicar de forma explícita al método AdministradorDeUsuario.ObtenerInstancia utilizando un generic, algo que por supuesto no es deseado ya que obliga al cliente de la aplicación a estar conciente de los tipos de usuario que existen y esto limita la aplicación a trabajar con los usuarios existentes y no nos permite agregar nuevos usuarios sin tener que recompilar la aplicación.

En segundo lugar estamos utilizando clases para los usuarios que no contienen datos y por lo tanto no son inicializados cuando se crea la instancia, por lo que el método Autorizar no funcionaría en un escenario real ya que no tiene los datos que el usuario utilizó para loguearse.

Mejoras

Para atacar el primer problema vamos a cambiar la forma en que le solicitamos nuestras instancias a Unity. Con los contenedores de IoC tenemos la gran ventaja de que podemos solicitarles que resuelvan el tipo en base al nombre del mapeo que queremos utilizar. El primer paso es cambiar el archivo de configuración para que trabaje con un solo contenedor de la siguiente forma:

<?xml version="1.0" encoding="utf-8" ?>
<
configuration>
<
configSections>
<
section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
</
configSections>
<
unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<
alias alias="IUsuario" type="Core.LogicaDeNegocios.IUsuario, Core.LogicaDeNegocios" />
<
namespace name="Core.LogicaDeNegocios" />
<
assembly name="Core.LogicaDeNegocios" />
<
container>
<
register type="IUsuario" name="usuario" mapTo="Usuario"
/>
<
register type="IUsuario" name="usuarioWeb" mapTo="Usuario_Web"
/>
</
container
>
</
unity>
</
configuration>



Seguidamente quitamos el parámetro del tipo que estamos solicitando en el método ObtenerInstancia ya que como dije anteriormente vamos a dejar que Unity resuelva el tipo en base al nombre del mapa que queremos utilizar. Los posibles nombres para los mapas son:



<register type="IUsuario" name="usuario" mapTo="Usuario" />
<
register type="IUsuario" name="usuarioWeb" mapTo="Usuario_Web" />



Además al método ObtenerInstancia le vamos a agregar dos parámetros más, el usuario y el password para que pueda agregarlos como valores al usuario que se vaya a crear – problema 2. La invocación del método desde nuestra pantalla de login ahora se ve así:



string _tipoUsuario = string.Empty;
if (cmbEmpresa.SelectedItem.ToString() == "Empresa A")
_tipoUsuario = "usuario";
else
_tipoUsuario = "usuarioWeb";

UsuarioActual = AdministradorUsuario.ObtenerInstancia(_tipoUsuario, txtNombreUsuario.Text, txtPassword.Text).Autenticar();



El siguiente paso y para trabajar en el problema 2, es crear un constructor para cada tipo de usuario de forma tal que reciba el usuario y el password como parámetros y que pueda hacer la autenticación con los mismos. Las clases modificadas son las siguientes:



public class Usuario_Web: IUsuario
{

public Usuario_Web(string pNombreUsuario, string pPassword)
{
Trace.WriteLine(string.Format("Datos del usuario. NombreUsuario: {0}, Password: {1}", pNombreUsuario, pPassword));
}


public IUsuario Autenticar()
{
Trace.WriteLine("Autenticar Usuario Web");
return this;
}

public void Autorizar(object _form)
{
Trace.WriteLine("Autorizar Usuario Web");
}
}



public class Usuario : IUsuario
{
public Usuario(string pNombreUsuario, string pPassword)
{
Trace.WriteLine(string.Format("Datos del usuario. NombreUsuario: {0}, Password: {1}", pNombreUsuario, pPassword));
}


public IUsuario Autenticar()
{
Trace.WriteLine("Autenticar Usuario");
return this;
}

public void Autorizar(object _form)
{
Trace.WriteLine("Autenticar Usuario");
}
}



Ahora solo nos queda modificar el método ObtenerInstancia para que a través de Unity se pueda resolver el tipo que deseamos instanciar en base al tipo de usuario que queremos utilizar y además que los datos del usuario puedan ser inyectados a través del constructor de la clase a instanciar:



using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;

namespace Core.LogicaDeNegocios
{
public static class AdministradorUsuario
{
public static IUsuario ObtenerInstancia(string mapName, string pUsuario, string pPassword)
{
using (IUnityContainer container = new UnityContainer())
{
container.LoadConfiguration();
return container.Resolve<IUsuario>(mapName, new ParameterOverrides { { "pNombreUsuario", pUsuario }, { "pPassword", pPassword } });

}
}
}
}



Con esto ya podemos ejecutar nuestra aplicación y crear la instancia que deseamos con los datos enviados.



image



image



Technorati Tags: ,,,,

2 comentarios:

Arturo dijo...

Muy interesantes posts. Muchas gracias.

PD: Talvez se pudo hacer lo siguiente: El name de los register del container han podido ser: "Empresa_A" y "Empresa_B".
Luego cargábamos el combo con los registers dinámicamente [talvez con replace('_', ' ')] y podríamos evitar recompilar cuando adicionemos una nueva implementación de IUsuario.

Luis D. Rojas dijo...

Hola Arturo. Efectivamente en los siguientes post que ya estoy preparando de IoC vamos a poner las clases en dlls aparte y vamos a hacer la carga dinámica y sin recompilar. Espero tus comentarios :)