domingo, 21 de febrero de 2010

Acceso a datos

¿Cómo acceder a nuestra Base de Datos? ¿Cómo organizar las clases?

Empezare por comentar que la creación de nuestras clases y componentes debe buscar seguir un principio “alta cohesión y bajo acoplamiento”. Esto quiere decir que no debemos tener clases que sean todologas, por el contrario deben aislar la responsabilidad de una funcionalidad especifica. ¿Han manejado código con sentencias DML en el front End, les suena? Bueno eso es exactamente una de las cosas que no debemos hacer .
¿Tres capas, como es? Es separar la responsabilidad de nuestra aplicación en tres grandes bloques:
1)Acceso a datos
2)Capa de negocio
3)Front-End , que es nuestra interfaz.

Han manejado reglas de negocio directamente en un formulario Windows o Web? Eso es lo que debemos evitar. ¿Y si un día nos piden que hagamos un proceso Batch que involucran a nuestras clases o una aplicación Windows cuando era Web y tenemos reglas del negocio en todas partes? A eso me refiero con aislar responsabilidades.

Capa de Acceso a datos
Crearemos un proyecto de librería de clases , para el ejemplo usare el proveedor de datos de SQL, existe EnterpriseLibrary 4 que nos da otras bondades pero eso lo veremos en otra entrega
Agregar el espacio de nombres a la clase
Agregar la referencia a System.Configuration
Por ahora a nuestro componente de acceso a Datos solo le agregare un método para la ejecución de store procedure (executeNonQuery).
Las clases que deben agregar son :

NOTA , TODAS LA IMAGENES SE AGRANDAN AL DAR CLICK SOBRE ELLAS


Clase Parameter ,esta clase nos permitira "envolver" lo que normalmente hariamos con objectos de tipo SqlParameter, esto lo hice asi, pensando en un futuro modificar el componente de acceso a datos para cualquier proveedor, es decir al consumidor le damos un objecto de tipo parametro pero generico.

Clase DataAcces : Tendra metodos para realizar peticiones a la Base de datos (executeNonQuery, ExecuteScalar,etc..), habiamos platicado de los parametros que requerimos pasar a una petición a la BD,¿como pasarlos?Finalmente es una colección, recurriremos a Generics usando un diccionario personalizado del tipo la clase parametro.

La clase la prepararemos de tal forma que nos permita manejar transacciones. exhibiendo un metodo para iniciar una transacción, un metodo para confirmarla, y un metodo para rechazarla, nuestra clase ademas nos permitira establecer el nivel de aislamiento con el que queremos que se efectuen nuestras peticiones a la Base de Datos, en otra entrega hablaremos de los diferentes niveles de aislamiento.

Tambien implementaremos la interfaz IDisposable para prevenir que nuestro componente deje conexiones abiertas en la Base de Datos




Capa de Negocio

Agregaremos otro proyecto de clases a nuestra solución que nos servirá como capa de negocio. El cual deberá tener una referencia a nivel proyecto del componente de acceso a datos.

Nuestra capa de negocio debe tener implementadas todas las reglas de negocio, si con el tiempo nos pidieran hiciéramos un proceso Batch por ejemplo de facturas , imaginen el costo de tener en el front-end las reglas de cómo hacer una factura (por poner un ejemplo) .


En nuestro componente de acceso a datos implementaremos una práctica denominada “Inversor de control”, que nos permite “acoplar” fuertemente dos clases ,se que suena a contradicción con lo que escribir arriba pero les explicare: Hemos mencionado que no queremos crear clases todologas, esta práctica nos permitirá mover la responsabilidad de crear una instancia de una clase concreta a su manejador .


.

Es decir tendremos una clase que exhibe las propiedades de un cliente, y otra clase que sabe grabar, actualizar, abrir un cliente.
1)Agregar una clase que contendra las propiedades del cliente

2)Agregar una clase que contendrá solo los métodos que reciben o devuelven un cliente para operarlo.





El escenario comun cuando tenemos un catalogo(o al menos el que mas he visto) es que se crea una clase con las propiedades, en ella misma los metodos para manipular la información del catalogo, la mayor parte de las veces usando parametros para cada campo en un metodo grabar, eso NO es una buena practica.

¿Como trabaja por citar un ejemplo el proveedor de datos de sql? Con objectos, de esa manera empezamos a garantizar que lo que grabo es un cliente , un objecto cliente.

Espero no hacer confusa esta parte, para que este ejemplo sirva un poco mas , crearemos un método que grabe registros (uno por uno) y otro método que pase un XML con el contenido de lo que deseamos grabar.

3)Crear una tabla (con la que trabajaremos el ejemplo :


4)Crear un store procedure para administrar las excepciones (SQL 2005/2008)


5)Crear un store procedure para grabar registro por registro
5)Crear un store procedure para grabar registro por registro , creamos otro store solo para ejemplificar las transacciones,aunque pudiera ser el mismo.


6)Crear un store procedure para grabar en una sola petición el contenido de un XML



Capa de presentación

La capa de presentación pudiera ser un proyecto Web, windows o cualquier otro que nos ofresca una UI

Agregamos una referencia al componente de negocio
El siguiente paso que tenemos es consumir nuestras clases en el Front-End

private void grabar()
{

try
{
MCliente objMcliente = new MCliente();
ICliente objCliente;
objCliente = objMcliente.Create();
objCliente.Apellido = "MiApellido";
objCliente.Nombre = "MiNombre";
objCliente.Observaciones = "MisObservaciones";
objMcliente.Save(objCliente);
}
catch (Exception)
{

throw;
}
}



Esto es solo el principio, cuando hablamos de capas, lo que queremos es sobre todo aislar resposabilidades. El tema es amplio, este ejemplo solo pretende resumir en breves pasos como debemos ordenar nuestro proyecto en capas.
Si desean descargar el codigo lo pueden bajar de la dirección :

2 comentarios:

  1. Hola Carlos;
    He revisado un poco el ejemplo que has puesto, y me surge una duda:
    Para realizar las transacciones, no te faltaría asignar dicha transaccion a la ocnexión? es decir, en la funcion BeginTrans() incluir:
    this.ObjTransation = this.ObjConnection.BeginTransaction(TIL)
    y en executenonquery:
    ObjCommand.Transaction = ObjTransaction;

    Gracias por el ejemplo, de todas formas.

    ResponderEliminar
  2. gracias por la observacion, ya lo corregi.

    ResponderEliminar