11.29.2010

Utilizando Unity para Resolver un Problema de Negocio - 3

Siguiendo con el uso de Unity para trabajar problemas de negocios de una forma menos dependiente entre los componentes que desarrollamos, vamos ahora a tratar el caso en donde la clase recién instanciada tiene una referencia a otra clase que a su vez debe ser resuelta por el contenedor de IoC.

Supongamos que cada usuario tiene una relación de agregación con la interface IEmpresa y que dependiendo de la empresa se paga un cargo diferente cuando se realiza alguna transacción. La interface IEmpresa tendrá un método que solamente le aplica el cargo a la transacción. La relación de clases en el problema actual es la siguiente:

image

En nuestro código la implementación de nuestras clases lucirá así:

Interface IEmpresa

namespace Core.LogicaDeNegocios
{
public interface IEmpresa
{
void AplicarCargos();
}
}



Clase Oficina: Implementa IEmpresa



using System.Diagnostics;

namespace Core.LogicaDeNegocios
{
public class Oficina: IEmpresa
{
public void AplicarCargos()
{
Trace.WriteLine("Aplicar Cargos Oficina");
}
}
}


Clase EmpresaExterna: Implementa IEmpresa



using System.Diagnostics;

namespace Core.LogicaDeNegocios
{
public class EmpresaExterna: IEmpresa
{
public void AplicarCargos()
{
Trace.WriteLine("Aplicar Cargos Empresa Externa");
}
}
}



En la interface IUsuario agregamos la relación de agregación antes representada:



public interface IUsuario
{
IEmpresa Empresa { get; set; }

IUsuario Autenticar();
void Autorizar(object _form);
}



Ahora modificamos la clase Usuario, para que tenga una implementación de IEmpresa. Nótese que la clase debe implementar la propiedad expuesta en la interface y no necesariamente el mismo tipo en el atributo, ya que con solo tener una clase que implemente la interface, ya podemos crear la asociación.



public class Usuario : IUsuario
{
Oficina _oficina;

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");
}

public IEmpresa Empresa
{
get
{
return _oficina;
}
set
{
_oficina = value as Oficina;
}
}

}



Lo mismo ocurrirá con la clase Usuario_Web, con la diferencia que en Usuario_Web si vamos a crear un atributo del tipo IEmpresa.



public class Usuario_Web: IUsuario
{
IEmpresa _empresa;

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

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

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

public IEmpresa Empresa
{
get
{
return _empresa;
}
set
{
_empresa = value;
}
}
}


Por otro lado, vamos a crear una clase que hereda de Usuario_Web y que implementa su propia implementación de IEmpresa.



public class OtroUsuario : Usuario_Web
{
IEmpresa _empresa;

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

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

public IEmpresa Empresa
{
get
{
return _empresa;
}
set
{
_empresa = value;
}
}

}



Por último agregamos el nuevo mapping para tenerlo entre la lista de clases que pueden ser resueltas.



  <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" />
<
register type="IUsuario" name="otroUsuario" mapTo="OtroUsuario" />
</
container>
</
unity>



En la parte del UI, agregamos el nuevo tipo de usuario a través de la empresa C.



private void btnLogin_Click(object sender, EventArgs e)
{
string _tipoUsuario = string.Empty;
if (cmbEmpresa.SelectedItem.ToString() == "Empresa A")
_tipoUsuario = "usuario";
else if (cmbEmpresa.SelectedItem.ToString() == "Empresa B")
_tipoUsuario = "usuarioWeb";
else
_tipoUsuario = "otroUsuario";

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

}



Si ejecutamos el código tal y como esta – y llevando la continuación del post anterior – veremos lo siguiente:



image



Como vemos en la figura, la instancia de empresa del tipo que resolvió Unity esta en nulo. Sin duda no es el comportamiento que queremos. Pero como hacer para que la clase que estamos instanciando pueda resolver la empresa que desea en tiempo de ejecución? Para este problema en específico, Unity me permite crear inyección para las propiedades con lo cual puedo definir dentro del registro del tipo para mapear, como debe de cargarse una propiedad de la clase que se va a resolver. Para lograr esto vamos a modificar nuestro archivo de configuración de la siguiente forma:



  <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="IEmpresa" name="empresaMap" mapTo="EmpresaExterna" />
<
register type="IEmpresa" name="oficinaMap" mapTo="Oficina"
/>
<
register type="IUsuario" name="usuario" mapTo="Usuario" >
<
property name="Empresa" dependencyName ="oficinaMap" dependencyType="IEmpresa"></property
>
</
register
>
<
register type="IUsuario" name="usuarioWeb" mapTo="Usuario_Web" />
<
register type="IUsuario" name="otroUsuario" mapTo="OtroUsuario" >
<
property name="Empresa" dependencyName ="empresaMap" dependencyType="IEmpresa"></property
>
</
register
>
</
container>
</
unity>



Como podemos ver en el texto en negrita, primera vamos a crear dos mapas nuevos para los tipos recién creados que implementan la interface IEmpresa.



<register type="IEmpresa" name="empresaMap" mapTo="EmpresaExterna" />
<
register type="IEmpresa" name="oficinaMap" mapTo="Oficina" />



Seguidamente vamos a modificar los registros para los usuarios deseados y vamos a establecer la instancia que queremos sea inyectada en la propiedad del tipo de IEmpresa.



<register type="IUsuario" name="otroUsuario" mapTo="OtroUsuario" >
<
property name="Empresa" dependencyName ="empresaMap" dependencyType="IEmpresa"></property>
</
register>



      <register type="IUsuario" name="usuario" mapTo="Usuario" >
<
property name="Empresa" dependencyName ="oficinaMap" dependencyType="IEmpresa"></property>
</
register>





En este caso hemos establecido que cuando se vaya a crear una instancia del tipo Usuario la propiedad IEmpresa se instancie con el tipo Oficina, y si por lo contrario creamos una instacia del tipo OtroUsuario vamos a tener la propiedad IEmpresa con una instancia del tipo EmpresaExterna. Si creamos una instancia de Usuario_Web la instancia de la propiedad IEmpresa no se podrá resolver y por lo tanto será null.



Si seleccionamos Empresa C –> OtroUsuario el resultado será:



image



Si seleccionamos Empresa A –> Usuario el resultado será:



image



Y si seleccionamos Empresa B –> el resultado será:



image







Technorati Tags: ,,

No hay comentarios: