none
Usar una interface para la gestión de las conexiones de la BD RRS feed

  • Pregunta

  • Buenas tengo un proyecto con c# y sqlServer que me gustaria poder cambiar el motor de la BD para ello he pensado en crear una interface la gestión de la BD y me queda de esta manera

        public interface IConexion
        {
            void OpenConection();
            void CloseConnection();
            void ExecuteQueries(string Query);
            object ShowDataInGridView(string Query);
            DbDataReader DataReader(string Query);
     
        }
    
        public class conexionSQLServer : IConexion
        {
    
            string ConnectionString = "";
            SqlConnection conSQLServer;
    
    
            public void OpenConection()
            {
                conSQLServer = new SqlConnection(ConnectionString);
                conSQLServer.Open();
            }
    ...
    ...
    }

        class conexionSQlite : IConexion
        {
    
            string ConnectionString = "";
            SQLiteConnection conexionSQLite;
    
    
            public void OpenConection()
            {
                conexionSQLite = new SQLiteConnection(ConnectionString);
                conexionSQLite.Open();
            }
    
            public void CloseConnection()
            {
                conexionSQLite.Close();
            }
    ...
    ...
    }

    Ahora la idea es sustituir el código que tengo actualmente que es de este tipo por la interface pero no se como hacerlo

     public static Boolean Modificar(Clientes pClientes)
            {
                Boolean retorno = false;
                using (SqlConnection Conn = new SqlConnection(ConnString))
                {
                    Conn.Open();
                    SqlCommand Comando = new SqlCommand("update Clientes set nombre = @Nombre .... ", Conn);
    
    
                    Comando.Parameters.AddWithValue("@Nombre", pClientes.Nombre);
                    Comando.Parameters.AddWithValue("@Apellido1", pClientes.Apellido1);
                    Comando.Parameters.AddWithValue("@Apellido2", pClientes.Apellido2);
                    replicacion.formatSQL(Comando);
                    retorno = Convert.ToBoolean(Comando.ExecuteNonQuery());
                }
                return true;
            }
    No tengo claro que es lo que tengo que sustituir.

    Gracias

    martes, 2 de mayo de 2017 10:02

Respuestas

  • hola

    >>El problema que tengo precisamente es ese que se tiene que poder cambiar de una a otra mediante configuración

    bueno alli es donde entra en juego las librerias de IoC que mencione

    >>se como le tendria que pasar "algo" que dependiendo de la configuración usase los metódos de la clase conexionSQLServer o bien de la clase conexionSQlite

    bueno aqui depende, podrias hacer un resolver que lea un valor de app.config y retorne una instancia u otra, una especie de factory

    public class ConnectionFactory{
    
       public static IConection GetConnection(){
    
          string valconfig = ... /aqui lees la config
    
          if(valconfig == "sql"){
    
               return new conexionSQLServer();
    
          } else{
    
             return new conexionSQLite();
    
         }
    
       }
    
    }

    es un poco burdo, pero como primer aproximacion servir

    entonces lo usarias

    public class Form1 : Form{
    
       private readonly IConexion conexion = null;
    
       public void Form1(){
    
           conexion = ConnectionFactory.GetConection();
    
       }
    
       //resto codigo
    
    }

    ----

    Porque usas un static

    public static Boolean Modificar(Clientes pClientes)

    no aplica un static en instancias

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina

    martes, 2 de mayo de 2017 16:50

Todas las respuestas

  • hola

    Es que no solo debes tener la conexion sino todos los objetos, ya que si usas el SqlCommand solo podras utilizar sql server

    crear una interfaz solo para obtener la conexion no alcanza, deberias tener una que sea

    public class conexionSQLServer : IConexion
    {
    
    	string ConnectionString = "";
    	SqlConnection conSQLServer;
    
    
    	public void OpenConection()
    	{
    		conSQLServer = new SqlConnection(ConnectionString);
    		conSQLServer.Open();
    	}
    
    	public void Ejecutar(string query, List<DbParameter> parametros)
    	{
    		this.OpenConection()
    	
    		SqlCommand Comando = new SqlCommand(query, conSQLServer);
    
    		foreach(var p in parametros)
    		{
    			Comando.Parameters.Add(p);
    		}
    		
    		Comando.ExecuteNonQuery();
    		
    	}
    }

    la idea es que definas el

    void Ejecutar(string query, List<DbParameter> parametros)

    en la interfaz para poder ejecutar

    veras que se usa el Dbparameter que seria el objeto commun a todos los que usan ado.net

    Igualmente sigo pensando que esta forma sigue siendo incorrecta, deberias programar en capas y generar una especificacion para cada db aprovechando las ventajas de esta pasando como datos no queries sino entidades de negocio

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina

    martes, 2 de mayo de 2017 10:58
  • hola

    Es que no solo debes tener la conexion sino todos los objetos, ya que si usas el SqlCommand solo podras utilizar sql server

    crear una interfaz solo para obtener la conexion no alcanza, deberias tener una que sea

    public class conexionSQLServer : IConexion
    {
    
    	string ConnectionString = "";
    	SqlConnection conSQLServer;
    
    
    	public void OpenConection()
    	{
    		conSQLServer = new SqlConnection(ConnectionString);
    		conSQLServer.Open();
    	}
    
    	public void Ejecutar(string query, List<DbParameter> parametros)
    	{
    		this.OpenConection()
    	
    		SqlCommand Comando = new SqlCommand(query, conSQLServer);
    
    		foreach(var p in parametros)
    		{
    			Comando.Parameters.Add(p);
    		}
    		
    		Comando.ExecuteNonQuery();
    		
    	}
    }

    la idea es que definas el

    void Ejecutar(string query, List<DbParameter> parametros)

    en la interfaz para poder ejecutar

    veras que se usa el Dbparameter que seria el objeto commun a todos los que usan ado.net

    Igualmente sigo pensando que esta forma sigue siendo incorrecta, deberias programar en capas y generar una especificacion para cada db aprovechando las ventajas de esta pasando como datos no queries sino entidades de negocio

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina

    Si tienes razón la duda que tengo es como lo invoco , asin no verdad?

    IConexion conexion = new IConexion();
                conexion.OpenConection();
                

    Gracias,

    martes, 2 de mayo de 2017 11:13
  • >>Si tienes razón la duda que tengo es como lo invoco , asin no verdad?

    mmm, no

    la instancia tiene que ser de una clase concreta

    IConexion conexion = new conexionSQLServer();
    conexion.OpenConection();

    o

    IConexion conexion = new conexionSQlite();
    conexion.OpenConection();

    en tu caso deberia hacer esto como ser

    public class Form1 : Form{
    
       private readonly IConexion conexion = null;
    
       public void Form1(){
    
           conexion = new conexionSQLServer();
    
       }
    
       //resto codigo
    
    }

    la idea seria que al iniciar el form instancies que tipo vas a usar

    con la ayuda de una libreria de IoC como ser Unity, Ninject, autofac, etc podrias configurar esto, pero bueno imagino eso sera un otra etapa

    despues podras cambiar facilmente de una instancia a otra en un solo punto sin afectar al resto del codigo

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina

    martes, 2 de mayo de 2017 11:24
  • >>Si tienes razón la duda que tengo es como lo invoco , asin no verdad?

    mmm, no

    la instancia tiene que ser de una clase concreta

    IConexion conexion = new conexionSQLServer();
    conexion.OpenConection();

    o

    IConexion conexion = new conexionSQlite();
    conexion.OpenConection();

    en tu caso deberia hacer esto como ser

    public class Form1 : Form{
    
       private readonly IConexion conexion = null;
    
       public void Form1(){
    
           conexion = new conexionSQLServer();
    
       }
    
       //resto codigo
    
    }

    la idea seria que al iniciar el form instancies que tipo vas a usar

    con la ayuda de una libreria de IoC como ser Unity, Ninject, autofac, etc podrias configurar esto, pero bueno imagino eso sera un otra etapa

    despues podras cambiar facilmente de una instancia a otra en un solo punto sin afectar al resto del codigo

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina

    Muchas gracias ahora lo entendí !!!

    El problema que tengo precisamente es ese que se tiene que poder cambiar de una a otra mediante configuración , es decir ahora tengo todo el código adaptado para que funcione con SQLServer y tal como he mostrado antes tengo por ejemplo este método

    public static Boolean Modificar(Clientes pClientes)
            {
                Boolean retorno = false;
                using (SqlConnection Conn = new SqlConnection(ConnString))
                {
                    Conn.Open();
                    SqlCommand Comando = new SqlCommand("update Clientes set nombre = @Nombre .... ", Conn);
    
    
                    Comando.Parameters.AddWithValue("@Nombre", pClientes.Nombre);
                    Comando.Parameters.AddWithValue("@Apellido1", pClientes.Apellido1);
                    Comando.Parameters.AddWithValue("@Apellido2", pClientes.Apellido2);
                    replicacion.formatSQL(Comando);
                    retorno = Convert.ToBoolean(Comando.ExecuteNonQuery());
                }
                return true;
            }

    Entiendo que para hacerlo "bien" de la forma que yo he propuesto en esta función Modificar no le tendria que pasar una conexión del tipo SQL  , no se como le tendria que pasar "algo" que dependiendo de la configuración usase los metódos de la clase conexionSQLServer o bien de la clase conexionSQlite

    Gracias,

    martes, 2 de mayo de 2017 11:34
  • hola

    >>El problema que tengo precisamente es ese que se tiene que poder cambiar de una a otra mediante configuración

    bueno alli es donde entra en juego las librerias de IoC que mencione

    >>se como le tendria que pasar "algo" que dependiendo de la configuración usase los metódos de la clase conexionSQLServer o bien de la clase conexionSQlite

    bueno aqui depende, podrias hacer un resolver que lea un valor de app.config y retorne una instancia u otra, una especie de factory

    public class ConnectionFactory{
    
       public static IConection GetConnection(){
    
          string valconfig = ... /aqui lees la config
    
          if(valconfig == "sql"){
    
               return new conexionSQLServer();
    
          } else{
    
             return new conexionSQLite();
    
         }
    
       }
    
    }

    es un poco burdo, pero como primer aproximacion servir

    entonces lo usarias

    public class Form1 : Form{
    
       private readonly IConexion conexion = null;
    
       public void Form1(){
    
           conexion = ConnectionFactory.GetConection();
    
       }
    
       //resto codigo
    
    }

    ----

    Porque usas un static

    public static Boolean Modificar(Clientes pClientes)

    no aplica un static en instancias

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina

    martes, 2 de mayo de 2017 16:50
  • Muchas gracias Leandro por tu tiempo y paciencia.

    De todas formas me quedan 2 dudas , disculpa la demora.

    La interface la debo definir en cada archivo de conexión o solo en uno de ellos?

    me refiero a este trozo de código

      public interface IConexion
        {
            void OpenConection();
            void CloseConnection();
            void ExecuteQueries(string Query);
            object ShowDataInGridView(string Query);
            DbDataReader DataReader(string Query);
        }
    

    La otra duda que tengo es , tomando este método como ejemplo como deberia adaptarlo para funcionar con  estas nuevas clases  ,

    public static Boolean Modificar(Clientes pClientes)
            {
                Boolean retorno = false;
                using (SqlConnection Conn = new SqlConnection(ConnString))
                {
                    Conn.Open();
                    SqlCommand Comando = new SqlCommand("update Clientes set nombre = @Nombre .... ", Conn);
    
    
                    Comando.Parameters.AddWithValue("@Nombre", pClientes.Nombre);
                    Comando.Parameters.AddWithValue("@Apellido1", pClientes.Apellido1);
                    Comando.Parameters.AddWithValue("@Apellido2", pClientes.Apellido2);
                    retorno = Convert.ToBoolean(Comando.ExecuteNonQuery());
                }
                return true;
            }
    

    El código quedaría de esta manera?

    public static Boolean Modificar(Clientes pClientes)
            {
                Boolean retorno = false;
                private readonly IConexion conexion = ConnectionFactory.GetConection();
    
                using (IConexion conexion = new ConnectionFactory.GetConection())
                {
                    conexion.Open();
                    SqlCommand Comando = new SqlCommand("update Clientes set nombre = @Nombre .... ", Conn);
    
    
                    Comando.Parameters.AddWithValue("@Nombre", pClientes.Nombre);
                    Comando.Parameters.AddWithValue("@Apellido1", pClientes.Apellido1);
                    Comando.Parameters.AddWithValue("@Apellido2", pClientes.Apellido2);
    
                    retorno = Convert.ToBoolean(conexion.Ejecutar());
                }
                return true;
            }
    
    


    Gracias

    miércoles, 3 de mayo de 2017 7:45
  • hola

    >>La interface la debo definir en cada archivo de conexión o solo en uno de ellos?

    la interfaz se implementa en cada clase que represente un motor de base de datos nuevo, en tu caso solo tienes dos sql y sqlite, pero si aparece otro alli tambien deberias implementarlo

    es justamente el que defin el contrato

    >>El código quedaría de esta manera?

    no, la instancia de conection solo la defines una ves, por eso es private y readonly, como puse en el ejemplo lo realzias desde el contructor del form

    public static Boolean Modificar(Clientes pClientes)
    {
    	
    	string query = "update Clientes set nombre = @Nombre .... ";
    	
    	List<SqlParameter> sqlParams = new List<SqlParameter>();
    	sqlParams.Add(new Sqlparameter("@Nombre", pClientes.Nombre));
    	sqlParams.Add(new Sqlparameter("@Apellido1", pClientes.Apellido1);
    	sqlParams.Add(new Sqlparameter("@Apellido2", pClientes.Apellido2);
    	
    	return conexion.Ejecutar(query, sqlParams);
    }

    adapta el Ejecutar() para que retorne el bool como respuesta

    saluos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina

    miércoles, 3 de mayo de 2017 11:24
  • Gracias por la respuesta , he adaptado el código y lo tengo de esta manera

    He modificado el Ejecutar para que el 2º paràmetro sea una lista de sqlParameters y no de DbParameter

        public interface IConexion
        {
            void OpenConection();
            void CloseConnection();
            void ExecuteQueries(string Query);
            object ShowDataInGridView(string Query);
            DbDataReader DataReader(string Query);
            void Ejecutar(string query, List<SqlParameter> parametros);
        }
    
    
        public class ConnectionFactory
        {
    
          public static IConexion GetConnection(){
          string valconfig = "sqlite";
    
    
          if(valconfig == "sqlite")
               return new conexionSQlite();
          else
              return new conexionSQLServer();
         }
    

    Y luego el método que accede a la BD ha quedado de esta forma

        public class ClientesBD
        {
    
            private readonly IConexion conexion = ConnectionFactory.GetConnection();
    
    
            public static Boolean Modificar(Clientes pClientes)
            {
                Boolean retorno = false;
                using (IConexion conexion = new ConnectionFactory.GetConnection())
                {
                    conexion.OpenConection();
                    string query = "update Clientes set nombre = @Nombre  ....";
    
    
                    List<SqlParameter> sqlParams = new List<SqlParameter>();
    	            sqlParams.Add(new SqlParameter("@Nombre", pClientes.Nombre));
    	            sqlParams.Add(new SqlParameter("@Apellido1", pClientes.Apellido1));
    	            sqlParams.Add(new SqlParameter("@Apellido2", pClientes.Apellido2));
    
                    conexion.Ejecutar(query, sqlParams);
                }
                return true;
            }
    
        }

    El error me lo da en la linia en negrita (la segunda) me dice

    Error 31 'classBD.clases.connexiones.ConnectionFactory.GetConnection()' es 'método' pero se utiliza como 'tipo'
    Error 32 'classBD.clases.connexiones.IConexion': el tipo utilizado en una instrucción using debe poderse convertir implícitamente en 'System.IDisposable'

    Gracias

    jueves, 4 de mayo de 2017 10:25
  • hola

    >>He modificado el Ejecutar para que el 2º paràmetro sea una lista de sqlParameters y no de DbParameter

    no, esto es incorrecto, si haces esto entonces no sera generico para usar con sql server y sqlite

    los parametros de la interfaz deben ser de una clase base por espo Dbparameter, si defines SqlParameter solo podrias pasar parametro que use sql server

    >>Y luego el método que accede a la BD ha quedado de esta forma

    me pregunto, estas analziando los ejemplos que puse anteriormente ? porque los estas cambiandod e forma incorrecta

    la asignacion de "conexion" se realzia en el constructor de la clase

    te olvidad del using y no vuelves a obtener la conexion

        public class ClientesBD
        {
            private readonly IConexion conexion = null;
    
    	public ClientesBD()
    	{
    		conexion = ConnectionFactory.GetConnection();
    	}
    
            public static Boolean Modificar(Clientes pClientes)
            {
                Boolean retorno = false;
    
    		conexion.OpenConection();
    		string query = "update Clientes set nombre = @Nombre  ....";
    
    		List<SqlParameter> sqlParams = new List<SqlParameter>();
    		sqlParams.Add(new SqlParameter("@Nombre", pClientes.Nombre));
    		sqlParams.Add(new SqlParameter("@Apellido1", pClientes.Apellido1));
    		sqlParams.Add(new SqlParameter("@Apellido2", pClientes.Apellido2));
    
    		conexion.Ejecutar(query, sqlParams);
                
                return true;
            }
    
        }

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina

    jueves, 4 de mayo de 2017 10:46
  • Gracias por tu ayuda , el código me ha quedado de esta manera

    public interface IConexion
        {
            void OpenConection();
            void CloseConnection();
            void ExecuteQueries(string Query);
            object ShowDataInGridView(string Query);
            DbDataReader DataReader(string Query);
            Boolean Ejecutar(string query, List<DbParameter> parametros);
        }
    
    
        public class ConnectionFactory
        {
          public static IConexion GetConnection()
          {
              string valconfig = "sqlite";
              if(valconfig == "sqlite")
                   return new conexionSQlite();
              else
                  return new conexionSQLServer();
          }
       }
    
    
    
    

    namespace classBD.clases.connexiones
    {
    
        public class conexionSQlite : IConexion
        {
            SQLiteConnection conexionSQLite;
    
    
            public void OpenConection()
            {
                conexionSQLite = new SQLiteConnection(ConnectionString);
                try
                {
                    conexionSQLite.Open();
                }
                catch (Exception ex)
                {
                    string error = ex.Message;
                }
            }
    
            public void CloseConnection()
            {
                conexionSQLite.Close();
            }
            
            ...
            ...
    
            public Boolean Ejecutar(string query,List<DbParameter> parametros)
            {
                Boolean retorno = true;
                this.OpenConection();
                SQLiteCommand Comando = new SQLiteCommand(query, conexionSQLite);
                foreach (var p in parametros)
                {
                    Comando.Parameters.Add(p);
                }
                try
                {
                    Comando.ExecuteNonQuery();
                }
                catch
                {
                    retorno = false;
                }
    
                return retorno;
            }
    
        }
    }

    Lo que me sigue fallando es esto

       public static Boolean ModificarTest(Clientes pClientes)
            {
                conexion.OpenConection();
                string query = "update Clientes set nombre = @Nombre ";
    
                List<DbParameter> sqlParams = new List<DbParameter>();
                sqlParams.Add(new DbParameter("@Nombre", pClientes.Nombre));
                sqlParams.Add(new DbParameter("@Apellido1", pClientes.Apellido1));
                sqlParams.Add(new DbParameter("@Apellido2", pClientes.Apellido2));
    
                Boolean retorno = conexion.Ejecutar(query, sqlParams);
                return retorno;
            }

    Le debo pasar una lista de DbParameter y con este formato me dice que es erroneo

    Error 5 No se puede crear una instancia de la clase o interfaz abstracta 'System.Data.Common.DbParameter'  

    Gracias


    • Editado golfgti6 viernes, 5 de mayo de 2017 7:19
    viernes, 5 de mayo de 2017 7:02