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.
Este conjunto de clases específicas heredan de unas clases más genéricas. Así, SqlConnection heredará de DbConnection, que no es más que una clase genérica con la funcionalidad de conectarse a una base de datos genérica. En la introducción a Abstract Factory, SqlConnection equivaldría a un «Motor Gasolina», mientras que DbConnection sería un «Motor». Nuestra
aplicación (el vehículo) necesita precisamente eso: un motor. Y se lo daremos a través de una factoría abstracta proporcionada por ADO.NET: DbProviderFactory.
Nuestro objeto DbProviderFactory será creado para una base de datos en concreto (es decir, crearemos una factoría concreta para SQL Server, Oracle, MySQL…). No obstante, el tipo instanciado dependerá ni más ni menos que de un parámetro de configuración en vez de estar codificado directamente. De forma dinámica, y sin modificar ni una sola línea de código, seremos capaces de conectarnos a diferentes bases de datos. Será ADO.NET el encargado de averiguar con qué base de datos estamos tratando y, a partir de ahí, instanciar la factoría adecuada para crear los componentes que necesitamos (en el artículo anterior, la factoría de motores gasolina, factoría de motores diesel…).
Entendiendo el funcionamiento de ADO.NET
Antes de comenzar, debemos comprender la filosofía del siguiente ejemplo. Debemos tener claro lo siguiente: nuestro objetivo (actual) es realizar una consulta a una fuente de datos. Esa consulta será almacenada en una estructura de datos, por ejemplo, en un DataSet.
Para rellenar el DataSet, será necesario un objeto que adapte los datos desde la fuente hasta la aplicación. Éste objeto será un DbDataAdapter.
El DbDataAdapter, sin embargo, necesita saber qué ejecutar. Para ello hará uso de una orden de base de datos o DbCommand.
El DbCommand necesita también cierta información: una sentencia SQL o nombre de procedimiento almacenado para pasarle a la fuente de datos… y una conexión por la cual establecer el intercambio de datos. Esta conexión será un objeto de tipo DbConnection.
Por último, el DbConnection únicamente hará uso de una cadena de conexión para establecer el enlace entre aplicación y fuente de datos.
Efectuando una consulta
Para comenzar, deberemos añadir la cadena de conexión a nuestro web.config o app.config.
<?xml version="1.0" encoding="utf-8"?> <configuration> <connectionStrings> <add name="DefaultConnection" connectionString="Data Source=SQLSRV2005;Initial Catalog=TestDB;Persist Security Info=True;User ID=dani;Password=prueba" providerName="System.Data.SqlClient" /> </connectionStrings> </configuration>
A continuación declararemos e instanciaremos todos los objetos que hemos indicado previamente.
// Declaración de referencias DbConnection conexion; // Clase encargada de realizar una conexión a una fuente de datos DbCommand cmd; // Clase encargada de ejecutar sentencias SQL y procedimientos almacenados DbDataAdapter da; // Clase encargada de adaptar los datos obtenidos a una estructura local DataSet ds = new DataSet(); // Clase encargada de almacenar los datos
En este momento, necesitaremos acceder a nuestro web.config o app.config. Para ello, añadiremos una referencia al ensamblado System.Configuration (Click derecho sobre el proyecto > Agregar
referencia > .NET > System.Configuration) y añadiremos manualmente la inclusión del espacio de nombres en la cabecera de nuestro fichero fuente, así como otros espacios de nombres necesarios.
using System; using System.Configuration; using System.Collections.Generic; using System.Text; using System.Data; using System.Data.Common;
A continuación necesitaremos acceder a la configuración. Para ello obtendremos una referencia a la misma pasando como parámetro el nombre de la cadena de conexión. A partir de ella, ADO.NET obtendrá un proveedor, el cual pasaremos como parámetro al método GetFactory para instanciar una factoría del tipo concreto al que hace referencia nuestra cadena de conexión.
// Obtenemos la configuración a través del nombre de la cadena de conexión ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings["DefaultConnection"]; // Utilizamos la configuración para instanciar una factoría abstracta de la cual instanciar objetos. DbProviderFactory factoriaProveedor = DbProviderFactories.GetFactory(settings.ProviderName);
Una vez que tenemos la factoría, es hora de instanciar, a través de ella, los objetos que necesitamos: una conexión,una orden y un adaptador de datos.
// Instanciamos una conexión, una orden y un adaptador conexion = factoriaProveedor.CreateConnection(); cmd = factoriaProveedor.CreateCommand(); da = factoriaProveedor.CreateDataAdapter();
Finalmente, configuramos los objetos de forma que las piezas encajen: proporcionaremos una cadena de conexión a la conexión, una conexión y una sentencia SQL a la orden, y una orden al adaptador.
// Configuramos la orden SQL indicando la sentencia a ejecutar y la conexión con la que trabajar. cmd.CommandText = "SELECT * FROM Usuario"; cmd.Connection = conexion; // Configuramos el adaptador indicándole la orden a ejecutar. da.SelectCommand = cmd;
Por último, rellenamos el DataSet con el adaptador, con lo que obtendríamos los resultados buscados.
// Rellenamos el DataSet mediante el adaptador. da.Fill(ds);
Cambiando de proveedor
Si ahora quisiéramos utilizar una base de datos MySQL en lugar de SQL Server, lo único que deberíamos hacer sería modificar la cadena de conexión, de la siguiente manera:
<?xml version="1.0" encoding="utf-8"?> <configuration> <connectionStrings> <add name="DefaultConnection" connectionString="Dsn=127.0.0.1;database=prueba;option=0;port=0;uid=root" providerName="System.Data.Odbc" /> </connectionStrings> </configuration>
Mediante esta operación, la aplicación seguiría sin preocuparse de la base de datos subyacente, pero manteniendo operativa la portabilidad.
Disculpame,
Podrías poner como quedaría todo el código completo si no es mucha molestia?
Gracias!
Por supuesto, intentaré facilitarte el código en cuanto tenga un ratillo 🙂
Esta muy excelente el ejemplo pero seria de gran ayuda tener el codigo completo del ejemplo…
Gracias de Antemano.
Excelente amigo, tu trabajo ayuda bastante!! nada mas un favor, ¿donde se puede ver como queda el codigo completo?
Todo lo que hiciste se pudo haber realizado con Dependency Injection, ¿Podrías indicarme si es correcto? o ¿si se aplica con Abstract Factory, Cual es la diferencia?
SAludos