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