jueves, 21 de enero de 2010

Factoría abstracta en acceso a datos con C#

Luego de mis largas vacaciones (y demasiado alcohol en la sangre luego de las fiestas de fin de año) les traigo este articulo que se me quedo en el tintero como muchos otros.

En proyectos medianos y grandes sucede muy a menudo que uno de los requerimientos sea el que la aplicación pueda cambiar de motor de base de datos a la medida que esta vaya creciendo, es decir si el proyecto inicio con una base de datos mysql y la cantidad de datos que esta maneja crece muy rápidamente entonces con el tiempo necesitara un motor de base de datos mas grande como Oracle por ejemplo. El problema en proyectos de este tipo es que si llegara a suceder lo mencionado tendrán que reescribir la capa de acceso a datos para que pueda acceder al nuevo motor, lo cual representa perdida de tiempo y dinero (que en ocasiones es lo único que le interesa a los jefes, jejeje).

La solución a este problema es usar el patrón Abstrac Factory o en buen español Factoría Abstracta al momento de escribir nuestro código de acceso a datos, para lograr nuestro cometido asumiré que el lector ya tiene conocimientos de acceso a datos con ADO.NET así es que no me centrare en ese punto sino en describir el patrón en si

ALGO DE TEORIA SOBRE EL ABSTRACT FACTORY

El Patrón factoría abstracta es un Patrón de creación lo que significa que ellos abstraen el proceso de creación de instancias de una clase, lo cual disminuye el acoplamiento entre clases en un proyecto. El Patrón factoría abstracta abstrae el proceso de creación de instancias de familias de productos relacionados sin necesidad de especificar las clases concretas a las que pertenecen.

AHORA SI LA SOLUCION

Tomaremos 4 métodos comunes en el manejo de bases de datos los cuales son:

        
  • Conectar (Realiza la conexión con el motor de BD)
  • Cerrar (Cierra la conexión con el motor de BD)
  • Ejecutar (Ejecuta una sentencia de tipo Insert, delete o update en la BD)
  • Consultar (Ejecuta una sentencia de tipo Select en la BD)

Para iniciar escribiremos una clase de acceso a un motor de base de datos Oracle con el cual realizaremos el ejemplo y obviamente en esa clase implementaremos los 4 métodos mencionados en la lista, el único detalle a considerar es que no utilizaremos las clases nativas de oracle como OracleConection u OracleCommand ni nada que se le parezca utilizaremos las clases bases que proporciona ADO.NET para el acceso a datos osea DBConnection, DBCommand, etc.

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Text;
   4: using System.Data.Common;
   5: using System.Data.OracleClient;
   6: using System.Collections;
   7: using System.Data;
   8:  
   9: namespace csdatos
  10: {
  11:     public class oramsSgbd:Isgbd
  12:     {
  13:         public DbConnection MiConexion;
  14:  
  15:         public void conectar(String servidor, String basedatos, String usuario, String contrasena)
  16:         {
  17:             String NombreProveedor = "System.Data.OracleClient";
  18:             String ParametrosConexion = "Data Source=" + servidor + ";Persist Security Info=False;User ID=" + usuario + ";Password=" + contrasena + ";Unicode=True";
  19:             DbProviderFactory Factory = DbProviderFactories.GetFactory(NombreProveedor);
  20:             MiConexion = Factory.CreateConnection();
  21:             MiConexion.ConnectionString = ParametrosConexion;
  22:         }
  23:  
  24:         public void cerrar()
  25:         {
  26:             MiConexion.Close();
  27:         }
  28:  
  29:         public int ejecutar(string sentencia)
  30:         {
  31:             int resp;
  32:             MiConexion.Open();
  33:             DbCommand command = MiConexion.CreateCommand();
  34:             command.CommandText = sentencia;
  35:             resp = command.ExecuteNonQuery();
  36:             MiConexion.Close();
  37:             return resp;
  38:         }
  39:  
  40:         public DbDataReader consultar(string sentencia)
  41:         {
  42:             DbDataReader resp;
  43:             MiConexion.Open();
  44:             DbCommand command = MiConexion.CreateCommand();
  45:             command.CommandText = sentencia;
  46:             resp = command.ExecuteReader();
  47:             return resp;
  48:         }
  49:  
  50:     }
  51: }

       

Dos detalles a los que prestaremos atención: el primero es que la clase oramsSgbd implementa la interface Isgbd que no es mas que la especificación de los métodos de los que hablábamos líneas arriba, hacemos esto para que las clases que gestionaran los motores de bases de datos implementen obligatoriamente estos 4 métodos. El segundo detalle al que debemos prestarle atención es que el método conexión tiene como datos de entrada los parámetros para la conexión a la base datos la cual se capturara mas adelante de un archivo de configuración.


Y ahora implementaremos nuestra fabrica (Ojo que esta fabrica es bastante sencilla por motivos didácticos )

       
   1: using System;
   2: using System.Collections.Generic;
   3: using System.Text;
   4: using System.Xml;
   5: using System.Configuration;
   6:  
   7: namespace csdatos
   8: {
   9:     public class fabricaSgbd
  10:     {
  11:         public Isgbd dameSgbd()
  12:         {
  13:             Isgbd msgbd=null;   
  14:             
  15:             String tipoorigen = ConfigurationSettings.AppSettings["tipoorigen"];
  16:             String servidor = ConfigurationSettings.AppSettings["servidor"];
  17:             String basededatos = ConfigurationSettings.AppSettings["basededatos"];
  18:             String usuario = ConfigurationSettings.AppSettings["usuario"];
  19:             String password = ConfigurationSettings.AppSettings["password"];
  20:  
  21:             if (tipoorigen == "oraclexe"){
  22:                 msgbd = new oramsSgbd();
  23:             }
  24:             if (tipoorigen == "mysql"){
  25:                 msgbd = new mysqlSgbd();
  26:             }
  27:             if (tipoorigen == "postgresql"){
  28:                 msgbd = new postgresqlSgbd();
  29:             }
  30:             msgbd.conectar(servidor, basededatos, usuario, password);
  31:  
  32:             return msgbd;
  33:         }
  34:     }
  35: }


Esta clase recoge los parámetros de conexión de un archivo de configuración que podría ser el web.config por ejemplo y tiene los siguientes parámetros: tipoorigen, que indica el tipo de motor de base de datos que se usara (oraclexe, mysql, postgresql, etc. Pueden generar las clases que deseen ), Servidor que es el dominio o ip del servidor de base de datos, basedatos, que es el nombre de la base de datos a usar (no valido para oracle que se maneja por esquemas), usuario, que es el alias del usuario con los privilegios de acceso a la base de datos y su respectivo password.


Como seguramente se dieron cuenta el único chiste es un if que al leer el tipo de base de datos a usar instancia la que cumpla con la condición pasándole los parámetros de conexión respectivos y devuelve un objeto que implementa los métodos definidos en la interface (ojo con eso).


Bueno ahora solo nos falta la interface que es la siguiente

       
   1: using System;
   2: using System.Collections.Generic;
   3: using System.Text;
   4: using System.Data.Common;
   5: using System.Collections;
   6: namespace csdatos
   7: {
   8:     public interface Isgbd
   9:     {
  10:         int ejecutar(string sentencia);
  11:         void conectar(String servidor, String basedatos, String usuario, String contrasena);
  12:         DbDataReader consultar(string sentencia);
  13:         void cerrar();
  14:         
  15:     }
  16: }


y listo como se darán cuenta la interface solo declara los métodos que serán obligatorios para todas las clases que implementen accesos a motores de bases de datos.


La forma de usar nuestra factoría es la siguiente para empezar tenemos que ubicar en el web.config (asumiendo que es una aplicación web ), los parámetros de acceso al motor de base de datos mas o menos así



   1: <appSettings>
   2:         <add key="tipoorigen" value="oraclexe" />
   3:         <add key="servidor" value="orcl" />
   4:         <add key="basededatos" value="ORGANIC" />
   5:         <add key="usuario" value="organic" />
   6:         <add key="password" value="orga" />
   7: </appSettings>


y luego desde alguna parte de la aplicación que necesitemos instanciamos nuestra fabrica mas o menos así:


y listo cuando necesiten cambiar de base de datos solamente tienen que escribir una clase muy similar a la clase OramsSgbd que implemente la interfaz Isgbd por ejemplo MysqlSgbd y cambiar en el web.config oraclexe por mysql y debería de funcionar de la misma forma sin hacer mas cambios.


   1: DbDataReader resp;
   2: fabricaSgbd mifabrica= new fabricaSgbd();
   3: Utilitarios misutilitarios = new Utilitarios();
   4:             
   5: Isgbd msgbd = mifabrica.dameSgbd();
   6: resp = msgbd.consultar("select * from actividades where estado='H' order by fecha_inicio desc");
   7:  
   8: while (resp.Read()){
   9: //aqui el codigo para recorrer el dataset resultante
  10: }

Nota: Lo aquí presentado funciona con el lenguaje sql estándar que cumpla para todas las bases de datos. Si se va a utilizar modificaciones del sql estándar propias del motor de la base de datos existen otros métodos como por ejemplo crear un lenguaje intermedio que se traduzca al lenguaje propio del motor que se este utilizando y este lenguaje intermedio se implemente dentro de las clases que van en conjunto con la factoría abstracta (así funciona hibernate en java), pero eso ya es tema de otro post.


Por ahora me despido la otra semana posteo de nuevo