Utilización de Data Providers (DbProviderFactories)

Alguna vez se nos ha planteado el caso de hacer un programa que soporte varias bases de datos. Para ello podemos utilizar los Dataproviders de ADO.NET tal y como explico en este POST. 

 

  La jerarquía de clases provista por .net permite la utilización de proveedores de datos específicos como SqlClient para la conexión a SQL Server.

    Por encima de este provider específico existe toda una jeraquía de interfaces y clases abstractas que permiten a los fabricantes o incluso a nosotros mismos la extensión del modelo ADO.

    Es conveniente la utilización del provider específico de cada fabricante, así podemos utilizar por ejemplo:

                             System.Data.SqlClient

                             System.Data.OleDb

                             System.Data.Odbc

                             System.Data.OracleClient.

            

           Podemos ver por ejemplo el interface IDbConnection y la implementación en SqlConnection de System.Data.SqlClient

IDBConnection

sqlconnection

Esta libertad bien construida en ADO.NET nos permite por ejemplo realizar una aplicación que sea multi base de datos, es decir, que podamos preparar una capa de conexión, que en función de las necesidades por ejemplo pueda desplegarse para utilizar Oracle o Sql Server, Access. Teniendo en cuenta claro los temas específicos de cada base de datos o proveedor.

Los proveedores disponibles los encontramos en el fichero machine.config, el cual se ubica en la carpeta del framework, en mi caso C:WindowsMicrosoft.NETFrameworkv2.0.50727CONFIG

<system.data>
    <DbProviderFactories>

        <add name="Odbc Data Provider" invariant="System.Data.Odbc" description=".Net Framework Data Provider for Odbc" type="System.Data.Odbc.OdbcFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>

        <add name="OleDb Data Provider" invariant="System.Data.OleDb" description=".Net Framework Data Provider for OleDb" type="System.Data.OleDb.OleDbFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>

        <add name="OracleClient Data Provider" invariant="System.Data.OracleClient" description=".Net Framework Data Provider for Oracle" type="System.Data.OracleClient.OracleClientFactory, System.Data.OracleClient, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>

        <add name="SqlClient Data Provider" invariant="System.Data.SqlClient" description=".Net Framework Data Provider for SqlServer" type="System.Data.SqlClient.SqlClientFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>

        <add name="Microsoft SQL Server Compact Data Provider" invariant="System.Data.SqlServerCe.3.5" description=".NET Framework Data Provider for Microsoft SQL Server Compact" type="System.Data.SqlServerCe.SqlCeProviderFactory, System.Data.SqlServerCe, Version=3.5.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91"/>

</DbProviderFactories>

</system.data>

En el siguiente Ejemplo podemos ver la diferencia en utilizar un proveedor de forma específica (SqlClient para SQL Server) y de utilizar las clases DbProviderFactories del espacio de nombres System.Data.Common para recuperar el mismo proveedor.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;
using System.Data.SqlClient;

namespace DataProviders
{
    class Program
    {
        static void Main(string[] args)
        {
            DbProviderFactory factoria = DbProviderFactories.GetFactory("System.Data.SqlClient");

            DbConnection conexion = factoria.CreateConnection();
            conexion.ConnectionString ="Data Source=.\MSSQL2008STD;Initial Catalog=AdventureWorks;Integrated Security = SSPI";
            conexion.Open();
            Console.WriteLine("conexión creada");
            conexion.Close();

            SqlConnection conexionSql = new SqlConnection();
            conexionSql.ConnectionString="Data Source=.\MSSQL2008STD;Initial Catalog=AdventureWorks;Integrated Security = SSPI";
            conexion.Open();
            Console.WriteLine("conexión creada");
            conexion.Close();

        }
    }
}

 

Podríamos por ejemplo tener un escenario en que hacemos una aplicación pueda distribuirse con dos modalidades, una básica con Access o una Enterprise con Sql Server. Y además dejar preparado nuestro código para la futura incorporación de un nuevo tipo de base de datos.

Vamos a ver por ejemplo como haríamos si quisiéramos que nuestro código funcionara para Access o Sql Server (Lo siento no tengo Oracle instalado, jejeje)

Podemos utilizar la conocida AdventureWorks, en mi caso la importo desde desde Sql a Access para poder hacer el programa.

Para simplificar el ejemplo, usaremos una propiedad en la clase de Datos que servirá para indicar con un valor entero si queremos conectarnos a SQL Server o Access.

En la misma clase construiremos el método conectar, que en función del tipo de conexión establecerá la cadena de conexión necesaria y el Dataprovider.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;

namespace DataProviders
{

    class DataproviderEjemplo
    {
         int conecctionType;

public int ConecctionType
{
  get { return conecctionType; }
  set { conecctionType = value; }
}

public DbConnection Conectar()
{
    String cadenaConexion = "";
    String proveedor = "";
   switch (conecctionType){
       case 0:
           cadenaConexion = "Data Source=.\MSSQL2008STD;Initial Catalog=AdventureWorks;Integrated Security = SSPI";
           proveedor = "System.Data.SqlClient";
           break;
       case 1:
           cadenaConexion = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\javier\Documents\AdventureWorksAccess.accdb;Persist Security Info=False;";
           proveedor = "System.Data.OleDb";
           break;
   }

   DbProviderFactory factoria = DbProviderFactories.GetFactory(proveedor);
   DbConnection conexion = factoria.CreateConnection();
   conexion.ConnectionString = cadenaConexion;
   conexion.Open();
   return conexion;

}
    }
}

Un programa que utilice esta clase podría ser el siguiente, simplemente se intenta ilustrar cómo conecto a access o sql en función del valor establecido en la propiedad connectiontype comentada arriba.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;

namespace DataProviders
{
    class Program
    {
        static void Main(string[] args)
        {
            DataproviderEjemplo _obj = new DataproviderEjemplo();
             DbConnection conexion;
            //conectamos con sql server
            _obj.ConecctionType=0;
            conexion = _obj.Conectar();

            //conectamos con Access
            _obj.ConecctionType = 1;
            conexion = _obj.Conectar();

        }
    }
}

 

Para finalizar vamos a hacer un método que devuelva un datatable y que haga una consulta a una tabla (Employee). Se ejecutará sobre Access o SqlServer en función del valor del ConnectionType que hayamos asignado.

Aprovechando que el nombre de las tablas en access no pueden contener un “.”, pues en la función que devuelve el datatable evaluaremos si es sql o access para ejecutar una consulta sql u otra. Esto nos puede ser util para el caso de que la sentencia sql cambie de una base de datos a otra.

La clase incluyendo la función que consulta la tabla sería la siguiente:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;
using System.Data;

namespace DataProviders
{

    class DataproviderEjemplo
    {
         int conecctionType;

public int ConecctionType
{
  get { return conecctionType; }
  set { conecctionType = value; }
}

public DbConnection Conectar()
{
    String cadenaConexion = "";
    String proveedor = "";
   switch (conecctionType){
       case 0:
           cadenaConexion = "Data Source=.\MSSQL2008STD;Initial Catalog=AdventureWorks;Integrated Security = SSPI";
           proveedor = "System.Data.SqlClient";
           break;
       case 1:
           cadenaConexion = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\javier\Documents\AdventureWorksAccess.accdb;Persist Security Info=False;";
           proveedor = "System.Data.OleDb";
           break;
   }

   DbProviderFactory factoria = DbProviderFactories.GetFactory(proveedor);
   DbConnection conexion = factoria.CreateConnection();
   conexion.ConnectionString = cadenaConexion;
   conexion.Open();
   return conexion;

}
public DataTable GetReader(DbConnection conexion)
{

    DataTable tabla = new DataTable();
    DbCommand micommand = conexion.CreateCommand();
    switch (conecctionType)
    {
        case 0:
            micommand.CommandText = "select * from HumanResources.Employee";
            break;
        case 1:
            micommand.CommandText = "select * from HumanResources_Employee";
            break;
    }
    micommand.CommandType = System.Data.CommandType.Text;
    DbDataReader reader = micommand.ExecuteReader();
    tabla.Load(reader);
    return tabla;
}

    }
}

Y por último el programa que la utiliza puede ser el siguiente:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;
using System.Data;

namespace DataProviders
{
    class Program
    {
        static void Main(string[] args)
        {
            DataproviderEjemplo _obj = new DataproviderEjemplo();
             DbConnection conexion;
             DataTable tabla;

            //conectamos con sql server o access cambiando el vaor 0 a 1
            _obj.ConecctionType=1;
            conexion = _obj.Conectar();
            tabla=_obj.GetReader(conexion);
            foreach(DataRow row in tabla.Rows) {
                foreach (DataColumn col in tabla.Columns)
                {
                    Console.WriteLine(row[col].ToString());
                }
            }

            conexion.Close();

        }
    }
}

6 comentarios en “Utilización de Data Providers (DbProviderFactories)”

  1. Un artículo excelente. Claro y conciso, directo al concepto.

    Llevaba tiempo buscando un artículo así para introducirme en el tema de los Data Providers.

    ¡Enhorabuena una vez más!. Sin duda un artículo muy útil.

  2. Muy bien me ha gustado mucho. Tan solo un comentario ¿por que no utilizas “usings”, en la creación de las conexiones? yo tuve muchos problemas (con cierres de bd, por ejemplo), y hasta que no lo implemente, no lo solucione. Creo que es una buena práctica. Saludos.

  3. Hola, muy bueno tu ejemplo, tengo un proyecto el cual deseo trabajar con Mysql, sql server y oracle, ya estoy usando el concepto de factory, pero no me gusta el hecho de tener q hacer swicht dependiendo del motor de base de datos para cambiar la consulta (se q es por sintaxis, ya que algunas cambian), entonces q se puede hacer para generar algo mas generico

  4. Respondieno un poco la inquietud de Julio, prodías crear una entrada en el archivo de configuraciones (web.config o app.config) donde indiques el proveedor a utilizar:

    string proveedor = ConfigurationManager.AppSettings[“myProvider”];

    DbProviderFactory factoria = DbProviderFactories.GetFactory(proveedor);

    de igual forma para el ConnectionStrings

  5. me gustaria saber como hacer lo mismo pero para registrar o ingresar datos, ya intente de varias formas y nada, espero ,me puedas ayudar, por cierto muy interesante el tema xD

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *