Implementando IoC en ASP.NET MVC 2 con Castle Windsor

En un post anterior hablamos sobre los beneficios de aplicar el patrón IoC en nuestros proyectos y vimos un breve ejemplo utilizando Unity de la gente de Patterns & Practices. Además comentamos que existen diferentes frameworks que nos facilitan estas tareas, uno de ellos es Castle Windsor y es el que vamos a utilizar en este caso.

Castle Project

Castle Project

En este post vamos a explicar cómo configurar IoC utilizando este framework en una aplicación web ASP.NET MVC 2.

El primer paso será descargar desde la página oficial del proyecto Castle las librerías Windsor 2.1link de la descarga.

El siguiente paso será crear una aplicación ASP.NET MVC 2 y agregar las siguientes referencias a nuestro proyecto:  Castle.Core.dll, Castle.MicroKernel.dll, Castle.Windsor.dll.

Referencias

Referencias

Para seguir con el ejemplo del post inicial, vamos a crear una interfaz denominada IRedSocialService y dos servicios que la implementen (por ejemplo TwitterService y FacebookService). La definición de la interfaz podría ser la siguiente:

public interface IRedSocialService
{
    IEnumerable ObtenerContactos();
}

La implementación de ambos servicios se las dejo a ustedes 🙂 .

A continuación vamos a configurar nuestras dependencias utilizando Windsor Container. Para ello lo que vamos hacer es realizar algunas modificaciones en nuestro archivo de configuración Web.config y separar las configuraciones propias del contenedor en otro archivo de configuración aparte que vamos a crear y llamar Castle.config.

La estructura del proyecto sería más o menos la siguiente:

Estructura proyecto

Estructura proyecto

Al archivo web.config vamos a agregarle lo siguiente:

<configuration>
<configSections>
...
<section name="castle" type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />
</configSections>

<castle configSource="Castle.config"></castle>
 …
</configuration>

Qué estuvimos haciendo? En primer lugar indicando que la sección castle será utilizada por CastleSectionHandler y que es encuentra en el assemblie Castle.Windsor. En segundo lugar estamos indicando que la sección castle se encuentra dentro del archivo castle.config y que vamos a configurar a continuación.

Al archivo castle.config vamos a agregar lo siguiente:

<castle>
<components>
<component id="ServicioRedSocial"
                    type="Castle.Windsor.Logica.TwitterService, Castle.Windsor.Logica"
                    service="Castle.Windsor.Logica.IRedSocialService, Castle.Windsor.Logica" />
</components>
</castle>

Qué estuvimos haciendo? Estuvimos registrando nuestro primer elemento del contenedor. Vamos a explicar cada tag por parte:

  • id: nombre con el que queremos identificar ese elemento en particular.
  • service: la interfaz sobre la cual vamos hacer las inyecciones.
  • type: el tipo concreto que queremos que el contenedor devuelva como implementación de la interfaz definida.

Teniendo en cuenta la configuración actual, cuando solicitemos una instancia de IRedSocialService deberíamos obtener un objeto TwitterService. Pero ojo, aún falta para que esto suceda…

Ahora es turno de trabajar con nuestro controlador que llamaremos RedSocialController, y en donde vamos a definir nuestro contenedor y nuestro servicio de la siguiente manera:

private WindsorContainer contenedor;
private IRedSocialService servicio;

Ahora vamos a trabajar sobre el constructor del controlador y la acción Index :

public class RedSocialController : Controller
{
    private WindsorContainer contenedor;
    private IRedSocialService servicio;

    public RedSocialController()
    {
        this.contenedor = new WindsorContainer(new XmlInterpreter(new ConfigResource("castle")));
        this.servicio = contenedor.Resolve<IRedSocialService>();
    }

    public ActionResult Index()
    {
        IEnumerable contactos = servicio.ObtenerContactos();
        return View(contactos);
    }
}

Dentro del constructor del controlador creamos nuestro contenedor, indicándole que la sección castle del archivo de configuración tiene las configuraciones correspondientes. Luego seteamos nuestro servicio, pidiendo al contenedor que nos devuelva la instancia que hayamos configurado. Finalmente vamos a tener disponible nuestro servicio en cada una de las acciones de RedSocialController.

Ejecutamos y listo!

Ejemplo IoC

Ejemplo IoC

Si ahora deseamos recuperar los contactos de otra red social, solo deberemos modificar nuestro archivo de configuración y todo solucionado!

Pero porqué quedarse acá? Porqué no aprovechar algunas de las características de ASP.NET MVC y automatizar aún más nuestras inyecciones?… Comencemos entonces!

La idea ahora es hacer las inyecciones sobre los parámetros del constructor de los controladores, y evitar tener que crear en cada controlador el contenedor y luego setear los servicios. Pero cómo podemos hacer esto? …  ASP.Net MVC al crear un controlador utiliza una factory, una instancia de la clase DefaultControllerFactory que requiere que los controladores tengan el constructor por defecto. Cómo esto no nos sirve, ya que la idea es tener constructores con parámetros, podemos crear nuestra propia factory y luego especificarle a MVC que las utilice (tengamos en cuenta que deberá heredar de DefaultControllerFactory).

Entonces vamos a crear nuestra propia factory que se va a encargar de realizar las inyecciones de dependencias sobre los parámetros de nuestros controladores (y por nosotros :)). Para esto vamos a crear una clase denominada WindsorControllerFactory.

Veamos cómo queda nuestra factory:

public class WindsorControllerFactory : DefaultControllerFactory
{
    WindsorContainer container;

    public WindsorControllerFactory()
    {
        container = new WindsorContainer(new XmlInterpreter(new ConfigResource("castle")));
        var controllerTypes = from t in Assembly.GetExecutingAssembly().GetTypes()
                                     where typeof(IController).IsAssignableFrom(t)
                                     select t;

        foreach (Type t in controllerTypes)
            container.AddComponentLifeStyle(t.FullName, t,LifestyleType.Transient);
    }

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null)
            return null;

        return (IController)container.Resolve(controllerType);
    }
}

Como vemos debemos sobre-escribir el método GetControllerInstance e inyectar las dependencias al momento de devolver el controlador – esto en tiempo de ejecución.

Finalmente debemos indicarle a MVC que utilice nuestra controller factory, para eso agregamos en nuestro archivo global.asax la siguiente línea dentro del método Application_Start():

ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory());

Finalmente volvemos a redefinir nuestro controlador de la siguiente forma:

public class RedSocialController : Controller
{
    private IRedSocialService servicio;

    public RedSocialController(IRedSocialService servicio)
    {
        this.servicio = servicio;
    }

    public ActionResult Index()
    {
        IEnumerable contactos = servicio.ObtenerContactos();
        return View(contactos);
    }
}

A diferencia del controlador anterior, este tiene definido como parámetros del constructor el tipo de interfaz que vamos a utilizar en el servicio, la cuál es inyectada por nuestra factory al momento de inicializarse el controller. También podemos ver que no es necesario crear el contenedor y setear nuestro servicio, lindo no!?

Anuncios

Exportando a Excel desde .NET

Generalmente cuando trabajamos en .NET y tenemos que exportar a Excel nos encontramos con un problema del cual no encontré una solución “simple”. El mismo se trata de un “warning” cuando intentamos “abrir” el archivo generado:

Excel cannot open the file ‘filename.xls’ because the file format for the file extension is not valid. Verify that the file has not been corrupted and that the file extension matches the format of the file.

Warning

Warning

Este error ocurría con los clientes que tenían instalado la versión 2007 Microsoft Excel. Investigando un poco me encontré con diferentes origines del problema, miles de soluciones y ninguna efectiva.

Por lo tanto me puse a probar diferentes frameworks que realizaban exportaciones a Excel, y me encontré con NPOI que está basado en su versión Java, y la verdad que es muy completo. Resumiendo bastante,  NPOI es un proyecto open source que nos ayuda a leer/crear archivos xls, doc y ppt.

Descargadas las DLL correspondientes y referenciadas en mi proyecto ASP.NET MVC 2 me dispuse a probarlas, y efectivamente eliminaron ese molesto mensaje al abrir – con MS Excel 2007 – mi archivo creado 🙂 .

Les dejo a continuación un simple código para quienes deseen probar esta librería:

string filename = "grilla.xls";
Response.ContentType = "application/vnd.ms-excel";
Response.AddHeader("Content-Disposition", string.Format("attachment;filename={0}", filename));
Response.Clear();

HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet hoja1 = workbook.CreateSheet("Hoja1");

HSSFRow filaEncabezado = hoja1.CreateRow(0);
filaEncabezado.CreateCell(0).SetCellValue("Fecha");
filaEncabezado.CreateCell(1).SetCellValue("Nombre");
filaEncabezado.CreateCell(2).SetCellValue("Edad");

HSSFRow filaRegistro = hoja1.CreateRow(1);
filaRegistro.CreateCell(0).SetCellValue(DateTime.Now);
filaRegistro.CreateCell(1).SetCellValue("Sebis");
filaRegistro.CreateCell(2).SetCellValue(50);

MemoryStream file = new MemoryStream();
workbook.Write(file);

Response.BinaryWrite(file.GetBuffer());
Response.End();

SRP – Principio de responsabilidad simple

Cómo comente en el post anterior, la idea es ir desarrollando cada uno de los principios S.O.L.I.D. un poco más en profundidad. El primero que vamos a ver, es el principio SRP (Principio de responsabilidad simple o Single Responsability Principle).

Básicamente los que nos dice este principio es que “una clase debe tener una, y solo una razón para cambiar“.

Qué significa esto?

Bajo el principio SRP, una razón es una responsabilidad, y una clase debería tener solamente una responsabilidad. En caso contrario, deberíamos delegar estas responsabilidades a nuevas clases.

Vamos a ver un ejemplo bien sencillo que nos va a ayudar a entender un poco mejor de qué se trata todo esto.

Supongamos que estamos trabajando en nuestra versión del juego PACMAN. Una de las primeras cosas que podríamos hacer, es crear una clase que represente los personajes del juego. La clase Personaje, tendría un atributo que representa la posición actual del personaje en el escenario y una acción que le permita avanzar de posición siempre que sea posible:

public abstract class Personaje
{
    public Posicion Posicion { get; set; }
    public void AvanzarCasillero(Posicion nuevaPosicion){}
}

Otra función que debería cumplir es la de comer otros personajes, por ejemplo Pacman puede comer fantasmas (siempre y cuando coma la píldora grande), en tanto que los fantasmas tienen como objetivo comerse a Pacman. Además de comer otros personajes, Pacman puede comer píldoras. Por lo tanto, todas estas acciones deberíamos incluirlas también:

public abstract class Personaje
{
   public Posicion Posicion { get; set; }
   public void AvanzarCasillero(Posicion nuevaPosicion){ }
   public void ComerPersonaje(Personaje personaje){ }
   public void ComerPildora(Pildora pildora) { }
 }

Cómo ya nos habremos dado cuenta, podemos ver que la clase Personaje tiene más de una razón para el cambio, ya que “comer píldoras” es responsabilidad únicamente de Pacman, y no del Fantasma.

Para aislar el método que está provocando “más de una razón para el cambio”, deberíamos crear las clases Pacman y Fantasma y delegar las responsabilidades que correspondan:

public abstract class Personaje
{
    public Posicion Posicion { get; set; }
    public void AvanzarCasillero(Posicion nuevaPosicion){ }
    public void ComerPersonaje(Personaje personaje){ }
}

public class Pacman : Personaje
{
    public void ComerPildora(Pildora pildora){ }
}

public class Fantasma : Personaje
{
}

De esta forma, tenemos bien definidas las responsabilidades de cada clase y ya no tenemos razones (al menos por ahora) para cambiarlas.

Beneficios de aplicar este principio:

  • Código más fácil de escribir y de entender.
  • Menos propenso a errores, ya que cada clase tiene una única responsabilidad bien definida
  • Facilita las tareas de testing : tenemos bien definido que debemos testear en cada clase.
  • Facilita las tareas de mantenimientos
  • Hace que nuestro código sea más robusto y extensible.

Como desventajas podríamos comentar que no siempre es muy simple asociar una única responsabilidad a una clase. También la “exageración” de aplicar este principio, podría llevarnos al extreme de llegar a tener una clase por método!… lo que nos llevaría a tener una gran cantidad de clases sin sentido y que solo nos confundirían.

Principios S.O.L.I.D.

Los principios S.O.L.I.D. son un conjunto de diseños y buenas prácticas que se emplean en OOD y OOP (diseño y programación orientada a objetos).

Estos principios fueron desarrollados y publicados por primera vez por Robert “Tío Bob” Martin hace más de una década. Martin dice que la mayoría de nosotros usamos lenguajes orientados a objetos sin saber por qué, y sin saber cómo obtener el beneficio máximo de ellos. Por este motivo es que surgen estos patrones de diseño.

Aplicar estos principios nos ayudaran, entre otras cosas, a crear un código que sea más legible, simple, reusable, mantenible, escalable… es decir, nos ayudaran a evitar los problemas con los que nos solemos encontrar a medida que nuestro código va creciendo.

Los principios que lo componen son:

  • SRP: The Single Responsibility Principle (Principio de Responsabilidad Única)
  • OCP: The Open/Closed Principle (Principio Abierto / Cerrado)
  • LSP: The Liskov Substitution Principle (Principio de Sustitución de Liskov)
  • ISP: Interface Segregation Principle (Principio de Segregación de Interfaces)
  • DIP: The Dependency Inversion Principle (Principio de Inversión de Dependencias)

Cómo podemos ver S.O.L.I.D. surge del acrónimo de estos principios.

En las próximas entradas iremos hablando de cada uno de estos principios, nombrando además las ventajas y desventajas de cada uno.

Más información en el enlace de Robert Martin: PrinciplesOfOod

Solucionando error ‘Invalid temp directory’ de los ASP.NET Chart Control

Una de las cosas que me puso muy feliz es la facil integración de los controles ASP.NET Chart con ASP.NET MVC (en mi caso la versión 2.0). Trabajando de forma local no tuve inconvenientes con el control, pero al momento de publicar la aplicación apareció el siguiente error:

Invalid temp directory in chart handler configuration [c:\TempImageFiles\].

Investigando me encontré que el problema es una cuestión de configuración: el chart control busca dentro de nuestro web.config en las AppSettings una key llamada “ChartImageHandler”. Esta tiene definida cuestiones relacionadas a la forma de almacenamiento del chart, timeouts, y la ubicación física donde guarda la imagen generada. Por defecto el control crea la siguiente entrada en nuestro web.config :

<add key="ChartImageHandler" value="storage=file;timeout=20;dir=c:\TempImageFiles\;" />

En el caso de no que no haya creado la key en el web.config, la ubicación física por defecto donde crea las imagenes será el directorio “c:\TempImageFiles\”.

Cómo solucionar este problema?, agregando o modificando la siguiente key en nuestro web.config:

<add key="ChartImageHandler" value="storage=file;timeout=20;" />

Espero que les haya sido útil, saludos.