La capa DAL (Data Access Layer – Capa de Acceso a Datos)

Descargar Ejemplo

1.- Introducción

Seguro que más de una vez habéis oído hablar de la programación en 3 capas aplicada a Web. Ya sabéis, que si la capa de presentación (capa UI), la capa de negocios (capa BLL) o la capa de acceso a datos (capa DAL).

Gracias a su total orientación a objetos con C# (y otros lenguajes) y ASP.NET podemos trabajar con las 3 capas de forma bastante sencilla.

Según mi opinión, nos plantearemos la realización de un proyecto Web de tres maneras:

- Modo A: podemos pasar olímpicamente de las capas DAL y BLL, no queremos saber nada de ellas. ¡¡Simplemente queremos que nuestra Web quede chula!! Estás aquí dentro si usas SqlDataSource y como máximo te has permitido crear tú mismo las sentencias SQL.
- Modo B: queremos hacer las capas DAL y BLL... pero que nos cueste muy poquito o que se encargue de todo ello el Visual Studio. Es decir, que (como mucho) hemos creado los procedimientos almacenados, usamos el DataSet que provee el Visual Studio, o armándonos de valor, recogemos el dataset nosotros mismos y ponemos una clase entre la recogida del dataset y la presentación de datos (algo análogo hacemos con el insert, update y delete).
- Modo C: tenemos un control total y absoluto de todo lo que sucede en todo momento. Sólo en contadas excepciones usamos el DataSet (casi siempre el DataReader), tenemos nuestros procedimientos almacenados, e incluso evitamos al máximo usar el ObjectDataSource.

Desde luego esta clasificación no es ni mucho menos técnica, pero está basada en el conocimiento del trabajo práctico de muchos proyectos propios y ajenos. Y ni mucho menos hay que desmerecer ninguno del los tres modos.

El Modo A puede ser válido para una Web que queramos hacer en apenas un par de días.

El Modo B, puede ser completamente válido para Webs que no vayamos a modificar mucho ni requieran demasiada carga de trabajo. De hecho hay un muy buen tutorial aquí.

Pero el Modo C (sobre el que se va a basar este artículo), es el ideal para aplicaciones de gran carga de trabajo y que puedan ser fácilmente mantenibles, esto es, que en cualquier momento podamos añadir funcionalidades, tablas a la bases de datos, campos a nuestras tablas, procedimientos almacenados, etc.

Y de las varias formas de trabajar con el “Modo C”, yo os voy a proponer la mía. Ya sabéis que cada maestrillo tiene su librillo, y que ningún librillo es perfecto, por lo que en cualquier momento se aceptan sugerencias

Pero vayamos al grano. En el artículo vamos simplemente a ver una capa DAL genérica (en futuros artículos la completaremos con la capa BLL y la de presentación). Y cómo no, qué mejor que un ejemplo sencillito para explicarnos mejor. Es totalmente imprescindible que os bajéis el ejemplo para seguir correctamente las explicaciones.
 

2.- Nuestro ejemplo

Imaginemos una pequeña aplicación en la que queremos agrupar a usuarios dentro de una comunidad de usuarios. Una parte de nuestra aplicación consistirá en crear a usuarios (podemos utilizar los providers membership que vienen por defecto en ASP.NET), otra parte en gestionar las comunidades y otra en asignar usuarios a una comunidad.

Nosotros simplemente vamos a controlar la creación de comunidades de usuarios, así como la modificación, la selección y la eliminación de éstas.
 

2.1.- La base de datos

Por tanto, y simplificando mucho, proponemos una base de datos con la tabla (Comunidad de Usuarios) con dos simples campos (CU = ComunidadUsuarios):
- CU_Id: un autonumérico (int identity) que hará de clave principal.
- CU_Nombre: el nombre de la comunidad. (nvarchar(250)).


2.2.- La clase ComunidadUsuarios

En código, representaremos a la comunidad de usuarios mediante la clase “ComunidadUsuarios” (comunidadusuarios.cs) que -al ser un caso tan simple- sus propiedades coinciden con los campos de la base de datos (id y nombre).
 

2.3 La ConnectionString: constr

Antes de poder realizar acciones sobre la base de datos, hay que poder interactuar con con ella... y para eso tenemos que contectarnos. El ConnectionString es la llave que le dirá a nuestra aplicación todo lo necesario: tipo de base de datos, dónde se ubica ésta, nombre de usuario, contraseña, etc.

En nuestro ejemplo vamos a trabajar con SqlServer Express, ubicada dentro del directorio App_Data. Para conocer las connectionstring de cualquier tipo de base de datos, os recomiendo: http://www.connectionstrings.com/

Tal y como es habitual en ASP.NET, ubicaremos la definición de nuestro connectionstring en el archivo web.config (echadle un vistazo en el ejemplo), y accederemos rápidamente a su valor según lo especificado en la propiedad “constr” de nuestra clase “ComunidadUsuariosDAL”.
 

2.4.- La clase ComunidadUsuariosDAL

Las cuatro operaciones básicas de cualquier base de datos son las de creación (INSERT), selección (SELECT), modificación (UPDATE) y eliminación (DELETE). Sin embargo, en nuestra capa DAL sólo nos interesan 3 operaciones básicas:
- Listado/Selección de datos.
- Ejecutar sin requerir retorno.
- Ejecutar requiriendo un único valor.

Durante lo siguientes puntos vamos a describir cada una de las tres operaciones relacionándolas con nuestro ejemplo.

El código de todo el ejemplo está escrito para que sea compatible con diferentes bases de datos, no sólo con SQLServer. Para ello utilizaremos las clases del namespace “System.Data.Common”, y definiremos el tipo de base de datos desde nuestro web.config. De ese modo, usar este código para SQLServer, oracle, mySql, etc. Sólo supondrá cambiar el proveedor de datos desde nuestro web.config. Desde nuestra clase “ComunidadUsuariosDAL” accederemos a su valor desde la propiedad “myProvider”.
 

2.4.1.- Listado/Selección de datos

Aplicable cuando queremos recoger uno o varios registros de la base de datos.

Lo que nosotros queremos es poder acceder tanto a todas las comunidades de nuestra base de datos como a un única comunidad a partir de su Id.

Lo primero que debemos hacer es crear los procedimientos almacenados. En nuestro ejemplo, crearemos dos:
- CU_Select_All: devuelve todos los registros de la tabla ComunidadUsuarios
- CU_Select_ById: devuelve el registro según la Id que le pasemos.

Posteriormente creamos las funciones que se encargan de ejecutar los procedimientos almacenados y recoger los datos. Son “select_byId” y “select_All”.

Básicamente, lo que se hace en ambos casos sigue la siguiente estructura:
1.- Creamos la conexión, asignándole el ConnectionString. Para ello necesitamos el “DbProviderFactory”, con el que crearemos las conexiones y comandos según el provider que le hayamos especificado en el web.config (en nuestro caso “System.Data.SqlClient”). Lo hemos puesto como propiedad porque va a ser accedido desde varias funciones.
2.- Creamos el comando con el que vamos a trabajar, relacionándolo con la conexión y asignádole el procedimiento almacenado definido previamente. En el caso de “select_byId” se le añade el parámetro entero “id”.
3.- Según corresponda, recorremos los resultados con un datareader y los añadimos a una lista genérica, o recogemos el primer (y único) datareader.

Ahora ya tenemos lo que queríamos, una lista genérica con todas las comunidades de usuarios, o una ComunidadUsuarios según la CU_Id especificada.

NOTA: fijaos que hemos usado la sentencia “using”. Ésta se encarga de cerrar todos los recursos utilizados siempre (aunque surja un error). Es importante, porque dejarnos un simple datareader sin cerrar puede consumir muchos recursos.


2.4.2 Ejecutar sin requerir retorno

La ejecución si requerir retorno envío una orden con unos parámetros y no espera ningún resultado de la base de datos, por lo que es un modo de proceder muy rápido y limpio. Es aplicable cuando borramos un registro, lo modificamos o insertamos uno nuevo.

Conceptualmente podemos creer que “eliminar”, “modificar” o “insertar” son cosas diferentes, y que cada uno debe procederse de manera diferente... sin embargo como las tres responden a la definición de “enviar una orden (procedimiento almacenado) con parámetros sin esperar respuesta”, crearemos una función que les servirá a las tres “ejecutaNonQuery”. No sin antes haber creado los procedimientos almacenados correspondientes a cada una: “CU_Delete”, “CU_Update” y “CU_Insert”.

La función “ejecutaNonQuery” es muy similar a las creadas anteriormente: creamos conexión, creamos comando, añadimos los parámetros, etc. La única diferencia es que ahora el procedimiento almacenado y un listado de parámetros se lo pasamos como parámetros, y cuando antes hacíamos un “ExecuteReader()” para leer el datareader, ahora hacemos un “ExecuteNonQuery()”.

Mientras tanto, en las funciones “insert()”, “update()” y “delete()” se crean los parámetros necesarios, y se mandan junto con el nombre del procedimiento almacenado a ejecutar a “ejecutaNonQuery()”.
 

2.4.3 Ejecutar requiriendo un único valor

Hay ocasiones en las que querremos recoger un único valor de la base de datos. Por ejemplo, podríamos aplicarlo a una inserción de la que queramos saber el identificador creado. Otro ejemplo sería el de hacer un select que nos devolviera un solo dato.

En nuestro ejemplo, cuando hacemos un select de comunidades de usuarios según su id, se nos devuelve un solo dato, por tanto, a parte de lo hecho hasta ahora, podría hacerse de esta otra forma.

El modo de proceder será idéntico al de “ejecutar sin requerir retorno”. Tendremos una función central (“ejecutaScalar”) que recibirá el nombre del procedimiento almacenado y los parámetros necesarios. Lo único que cambiará es que llamará a “ExecuteScalar” en lugar de a “ExecuteNonQuery”, y que se devolverá un valor (tipo object, para hacerlo más genérico).

En nuestra función “select_byId2”, mandamos el parámetro “CU_Id” junto con el anteriormente creado procedimiento almacenado “CU_Select_ById”, y recogeremos el “nombre” de la comunidad. Sólo hay que pasarla a string, y devolver una instancia de “ComunidadUsuarios”.
 

3.- Ampliaciones

Como podréis imaginar, el código del ejemplo es totalmente académico. En muchos lugares y de muchos modos se podría optimizar el código para hacerlo más reutilizable.

Por ejemplo, podríamos crear una clase madre con las funciones básicas y hacer que todos heredaran de ella. Funciones como “EjecutaScalar()” o “EjecutaNonQuery()” serían las primeras candidatas a formar parte de esa clase madre.

Del mismo modo, en lugar de usar un código genérico que vale para diferentes tipos de bases de datos, podríamos usar código específico para SqlServer, Oracle y OleDb en general.

Tampoco se ha hecho en ningún momento comprobaciones de errores ni nada similar, algo que requeriría todo un artículo completo.
 

4.- El ejemplo

Para probar el ejemplo, se provee de “default.aspx”. Es importante reseñar que sólo se trata de probar el correcto funcionamiento de la capa DAL.

Si lo analizamos, vemos que default.aspx representa la capa de presentación, y ComunidadUsuariosDAL representa a la capa DAL: ¡¡¡no hemos creado una capa intermedia (la BLL) propiamente dicha!!! Sólo “default.aspx.cs” hace las veces de capa BLL, pero muy ligeramente.

“Default.aspx” está dividida en cinco partes, desde las que se puede:
1.- Insertar una nueva comunidad.
2.- Seleccionar una comunidad según su id.
3.- Modificar el nombre de una comunidad.
4.- Borrar una comunidad.
5.- Listar todas las comunidades.

Descargar Ejemplo