Patrones de Diseño

El Controlador en ASP.NET MVC 4 (II): Action Results


Hemos visto hasta ahora que nuestras acciones (recordemos que las acciones son los métodos públicos de un controlador) deben devolver un objeto de la clase ActionResult.

En ejemplos anteriores hemos utilizado los métodos View() y Content() para generar un objeto de este tipo, pero estos no son los únicos métodos que generan un resultado de una acción (ActionResult).

Dependiendo de la respuesta que queramos dar podremos hacer uso de los siguientes métodos, con sus correspondientes ActionResults, que generan desde texto plano (como Content()) hasta la lectura de un fichero (método File()) o la redirección a una nueva URL (método Redirect()):

MÉTODO ACTION RESULT DESCRIPCIÓN
Content() ContentResult Devuelve una cadena de texto.
EmptyResult (sin respuesta)
File() FileContentResult
FilePathResult
FileStreamResult
Devuelve el contenido de un fichero.
HttpUnauthorizedResult Devuelve un error de autorización (403)
JavaScript() JavaScriptResult Devuelve un fragmento de código JavaScript a ejecutar.
Json() JsonResult Devuelve datos en formato JSON.
Redirect() RedirectResult Redirecciona a una nueva URL.
RedirectToRoute()
RedirectToAction()
RedirectToRouteResult Redirecciona a otra acción.
View()
PartialView()
ViewResult
PartialViewResult
Traspasa el control a una vista.

A continuación echaremos un vistazo por encima a los elementos más importantes de esta tabla.

(más…)

El Controlador en ASP.NET MVC 4 (I): Enrutado


Anteriormente hemos aprendido el funcionamiento general de la arquitectura MVC de ASP.NET. Sin embargo, hay algunas dudas que se mantienen en el aire, como por ejemplo, cómo sabe MVC a qué controlador debe acudir cada vez que se recibe una petición HTTP.

Redireccionando las peticiones HTTP

Para bien o para mal, el enrutado no es una tarea que pertenezca al framework, sino que es ASP.NET quien debe encargarse de ello en última instancia. Previamente vimos que MVC 4 realiza una redirección hacia un controlador acorde a la ruta que se invoca: si se trataba de la ruta <servidor>/Home/About, comprobamos que se invocaba el método About() del controlador HomeController.

¿Dónde puede configurarse este comportamiento? La respuesta corta es: en el archivo de configuración global, global.asax.cs.

Este fichero contiene, entre otro código, las operaciones a realizar al levantar la aplicación, previas a cualquier petición que pueda realizarse. Entre esas operaciones se encuentra la posibilidad de indicarle a nuestra aplicación cómo se realizarán las redirecciones de las peticiones HTTP. Esto lo podremos observar en la siguiente línea de configuración:


RouteConfig.RegisterRoutes(RouteTable.Routes);

(más…)

Introducción a ASP.NET MVC 4


Aunque no hayas estudiado patrones de diseño, es altamente probable de que, si te dedicas a la programación, hayas oído hablar en alguna ocasión del Modelo-Vista-Controlador. Se trata de uno de los patrones de diseño más ubicuos y populares, que persigue la filosofía de crear un patrón de tres capas que diferencian las labores de la aplicación en:

  • MODELO: Gestiona la información que el usuario precisa, es decir qué información quiere el usuario.
  • VISTA: Muestra la información al usuario y permite que éste interactúe con ella, proporcionando datos, modificándolos, solicitando su modificación o simplemente, consultándolos. En resumen, cómo se muestra la información en pantalla.
  • CONTROLADOR: Actúa de intermediario entre los elementos anteriores y el usuario, atendiendo las peticiones HTTP (en el caso de ASP.NET), generando la información que el usuario requiere (modelo) y mostrándola por pantalla (vista).

Microsoft proporciona una implementación estándar de este patrón permitiendo crear un proyecto basado en web que genera automáticamente estos tres elementos, separando de una forma más clara la funcionalidad interna de la aplicación de la gestión de las peticiones y su visualización en pantalla.

(más…)

Abstracción de Datos (V): Aplicando el patrón Abstract Factory con C# y ADO.NET


Por norma general, un proyecto estará “atado” a una fuente de datos en particular (SQL Server, MySQL, Oracle…). Sin embargo, hay veces que esto no será así: la fuente de datos podría cambiar en cualquier momento, por lo que deberíamos ser capaces de, en la medida de lo posible, abstraer nuestra aplicación de nuestra base de datos.

Previamente comentábamos la flexibilidad otorgada por el patrón de diseño Abstract Factory. Hoy veremos una aplicación práctica, orientada precisamente a obtener práctica independencia de la fuente de datos mediante la utilización de clases comunes a todas las fuentes de datos.

Por norma general utilizamos clases específicas para lograr interactuar con una fuente de datos en concreto (por ejemplo, usaremos un objeto de la clase SqlConnection cuando trabajemos con SQL Server). Éstas clases específicas están optimizadas para trabajar con unos esquemas determinados, aportando un importante incremento del rendimiento de la aplicación. Pero como todo en esta vida, no es gratis. ¿Cuál es el precio? La portabilidad.

(más…)

Abstracción de datos (IV): El patrón Abstract Factory (Factoría Abstracta)


Anteriormente vimos lo que era una interfaz y cómo implementarla en C#. Hoy aprovecharemos estos conocimientos para explicar otro patrón de diseño: la Factoría Abstracta o Abstract Factory.

El patrón Abstract Factory es un patrón “familiar”. ¿Por qué familiar? Porque está especializado en proporcionar instancias de familias de objetos. Imaginemos la siguiente situación:

Abstract Factory

(más…)

Abstracción de datos (III): Utilizando Reflection para implementar un DAO en C#


El siguiente paso de nuestro objetivo de lograr un nivel de abstracción decente a la hora de implementar un modelo de datos consiste en la utilización del espacio de nombres Reflection. En una entrada anterior mostrábamos una función que nos devolvía un diccionario que contenía tanto el nombre como el valor de los filtros de la consulta.
Hoy vamos a ir más allá. Vamos a olvidarnos de la codificación de un DTO concreto: codificaremos un DAO que espere un objeto DTO genérico que cumpla los siguientes requisitos:

  • Cada Propiedad se corresponderá con un campo de la fuente de datos.
  • El nombre de la clase será DTO + Nombre de la tabla

Con estos dos requisitos, podremos codificar un DAO que comienza a abstraerse de los datos en sí, ligándose únicamente a una estructura arquitectónica y a una fuente de datos concreta (SQL Server). El siguiente paso será eliminar la segunda de estas restricciones, como veremos posteriormente.
Es importante partir de la entrada anterior para entender esta entrada, ya que no escribiré todo el código, sino que mostraré únicamente las modificaciones necesarias a los ficheros anteriores para poder entender el ejemplo.
DTOUsuario

La clase DTOUsuario no sufrirá modificación alguna. Esto es, será el DAO el que se adapte para conseguir la abstracción de un DTO en concreto.
DAOGenerico

En lugar de implementar las sentencias simples (SELECT, INSERT, UPDATE, DELETE) en cada uno de los DAO, crearemos una clase base llamada DAOGenerico en la que crearemos estas operaciones comunes. Posteriormente, las clases que hereden de ésta implementarán métodos específicos para cada entidad.
El primer cambio que tenemos en esta clase es la presencia de las funciones ObtenerElementos (visto en el primer post dedicado a Reflection) y ObtenerOrdenSql (vista en la anterior parte). Codificadas ambas funciones (manteniéndolas intactas), veremos cómo cambiamos la función select. El cuerpo de la misma será el siguiente:

public DataSet select(object datos)

Vemos que ya no esperamos un DTOUsuario, sino que nos basta un objeto cualquiera.

El siguiente paso será declarar los mismos elementos que en la versión anterior, así como comprobar que el objeto recibido no es nulo.

 // Instanciamos un DataSet, que albergará el contenido de la consulta
 DataSet ds = new DataSet();

// Declaramos dos StringBuilders: uno para la sentencia y otra para los campos
 // A su vez, declaramos un ArrayList para almacenar los parámetros.
 StringBuilder SQLString = new StringBuilder();
 StringBuilder Campos = new StringBuilder();
 ArrayList Parametros = new ArrayList();
 String NombreTabla = "";

 // Comprobamos que el DTO exista
 if (datos == null)
 throw new NullReferenceException("GenericDAO.select(datos)");

A continuación, recorremos los elementos del DTO, añadiendo los filtros en el caso de que éstos existan. Recordemos que Reflection nos proporcionaba la funcionalidad necesaria para almacenar en un diccionario el nombre y el valor de cada propiedad del objeto.

 Dictionary coleccionDatos = new Dictionary();
 // Obtenemos todas las propiedades del objeto
 coleccionDatos = ObtenerElementos(datos, MemberTypes.Property);

Ahora iteraremos cada elemento del diccionario, añadiendo, como hacíamos antes, elementos del tipo “Filtro = @Filtro”, solo que ahora el nombre del filtro viene dado por la clave del diccionario y su valor, por el contenido de el elemento que corresponde a esa clave.

 if (coleccionDatos != null)
 {
 foreach (string key in coleccionDatos.Keys)
 {
 if (coleccionDatos[key] != null)
 {
 Campos.Append(key).Append(" = @").Append(key).Append(" AND ");
 Parametros.Add(new SqlParameter("@" + key, coleccionDatos[key]));
 }
 }
 }

Como última novedad, obtendremos el nombre de la tabla a partir del nombre de la clase, eliminando del principio el prefijo “DTO”.

 // Creamos la cláusula SELECT
 SQLString.Append("SELECT * FROM ").Append((datos.GetType()).Name.TrimStart("DTO".ToCharArray()));

El resto del método es igual a la versión anterior.

 // Añadimos la cláusula WHERE, en caso de que sea necesaria
 if (Campos.Length > 0)
 {
 SQLString.Append(" WHERE ");

 // Como la cadena acabará en 'AND ', eliminamos los últimos 4 caracteres para cerrar la sentencia
 SQLString.Append(Campos.ToString().Substring(0, Campos.ToString().Length - 4));
 }

 // Obtenemos un SqlCommand configurado al efecto
 SqlCommand orden = ObtenerOrdenSql(SQLString.ToString(), Parametros);

 // Instanciamos un SqlDataAdapter y efectuamos la consulta
 SqlDataAdapter da = new SqlDataAdapter(orden);
 da.Fill(ds);

 // Por último, devolvemos el DataSet
 return ds;

Hecho esto, podemos crear un DTO por cada tabla de la base de datos. Siempre y cuando cumplamos las normas de respetar los nombres de tabla y columnas, esta función devolverá la consulta apropiada a cada entidad DTO llamando a la función select() y pasándole el objeto DTO como parámetro.

Abstracción de datos (II): Construyendo un DAO simple en C#


Como introducción a la aplicación práctica de este patrón que ya vimos previamente, daremos las indicaciones necesarias para construir un DAO simple para una entidad en concreto.

El ejemplo que mostraremos ahora se tratará de un elemento macizo y poco funcional, que posteriormente iremos retocando y refinando para alcanzar un nivel de abstracción lo suficientemente elevado como para exportarlo a un componente plenamente reutilizable.

(más…)

Abstracción de datos (I): El patrón DAO


Anteriormente vimos cómo rellenar un DataSet a través de un SqlCommand y un SqlDataAdapter. Haremos ahora una pequeña introducción a un patrón de diseño: el patrón DAO.

En software de computadores, un Data Access Object (DAO, Objeto de Acceso a Datos) es un componente de software que suministra una interfaz común entre la aplicación y uno o más dispositivos de almacenamiento de datos, tales como una Base de datos o un archivo. El término se aplica frecuentemente al Patrón de diseño Object [Wikipedia.org].

Con el permiso de los más puristas, un DAO no es otra cosa que un “adaptador” o un nexo entre la lógica de negocio y la capa de persistencia (generalmente, una Base de Datos). Lo que realiza este adaptador no es otra cosa que codificar la lógica de acceso a datos de una capa de persistencia en concreto.

¿Qué significa todo esto? Significa que por cada fuente de datos deberemos implementar un DAO específico para ella, dejando inmaculado el resto del código: cualquier cambio en la fuente de datos implicará la utilización de un DAO distinto, pero la lógica de negocio permanecerá indiferente ante tal cambio. El DAO es pues, una suerte de traductor entre el idioma que habla nuestra aplicación y el idioma que habla una fuente de datos específica (habrá un DAO para SQL Server, otro para MySQL, otro para ficheros, etc.)

La pregunta que surge ahora es la siguiente: ¿cómo realizamos esta traducción? Pues a través de una clase intermedia, que se encargará de “calcar” la estructura de una tabla de la fuente de datos y almacenará de forma temporal los datos de éste, al que llamaremos DTO o Data Transfer Object.

Patrón DAO

A partir de este momento, nuestra aplicación “entregará” la información que quiera compartir con la fuente de datos al DAO encapsulada en un DTO. El DAO entonces extraerá la información de esa cápsula y la codificará de forma que la fuente de datos la entienda (por ejemplo, escribiendo una sentencia SELECT e instanciando los objetos necesarios para comunicarse con la base de datos).

Como es lógico, al revés ocurrirá lo mismo: la fuente de datos entregará al DAO información. Éste se encargará de encapsularla de forma que nuestra lógica de negocio sea capaz de entenderla. De este modo, manteniendo una misma interfaz para los métodos del DAO (por ejemplo, un método SELECT que esperará siempre un objeto DTO), podremos cambiar la fuente de datos sabiendo que, si ésta cambia, únicamente será necesario modificar el “traductor” de la lógica de la aplicación a la de datos.

A riesgo de parecer repetitivo, los pasos que cumple un patrón DAO son los siguientes:

  1. Nuestra aplicación encapsula la información en un DTO.
  2. El DAO toma ese DTO, extrae la información y construye la lógica necesaria para comunicarse con la fuente de datos (sentencias SQL, manejo de archivos…).
  3. La fuente de datos recibe la información en el formato adecuado para tratarla.

En sentido contrario, ocurrirá lo mismo:

  1. Nuestra aplicación le envía al DAO un DTO vacío.
  2. El DAO realiza una petición de datos a la fuente de datos.
  3. La fuente de datos envía al DAO la información.
  4. El DAO recopila esa información, la encapsula en el DTO (o en otro elemento que la aplicación entienda) y se la devuelve a nuestra lógica de negocio.

Tras esta introducción teórica, en sucesivos envíos aprenderemos a implementar un patrón DAO en C#. A medida en que vayamos aprendiendo conceptos, comenzaremos a utilizar métodos más avanzados y sofisticados para conseguir una capa de persistencia lo más débilmente acoplada posible, todo ello mediante herramientas como ADO.NET, Reflection y el Data Access Application Block de la Enterprise Library.