简体   繁体   中英

How to manage multiple Winform instances with the same SQL Server 2012 connection?

I have a Winforms C# application that makes a lot of queries to a SQL Server 2012 database. This database has not foreign keys and in the winforms application i have a Data layer that manages the connection to DB using System.Data.Common.DbConnection object

This Winform application when is open, it has a Timer on the main form that maintains the connection open like this:

private void timerConexion_Tick(object sender, EventArgs e)
        {
            bool todoOK = false;
            if (this.Datos != null)
                if (this.Datos.Conexion != null)
                    if (this.Datos.Conexion.State == ConnectionState.Open)
                    {
                        todoOK = true;
                    }
            if (!todoOK)
            {
                this.timerConexion.Enabled = false;
                // The application must be close
                MessageBox.Show("¡Connection with the server is lost!\r\nThe application will be closed.", ConfigurationManager.AppSettings["TitleMessageBox"], MessageBoxButtons.OK, MessageBoxIcon.Stop);
                Application.Exit();
            }
        }

My problem is that i have like 20-30 PC's runing this Winform application and several times i'm having transaction locks and i have to kill process to releases those locks. I'm thinking to create a better connection to the DB but i dont know what's the best option... if i use Entity Framework it will a pain.. because i dont have any FK on my DB.

What's my best option?

This is my Data layer:

[CLSCompliant(true)]
    public class Datos
    {

        private DbConnection conexion = null;

        public DbConnection Conexion
        {
            get { return conexion; }
        }

        private DbCommand comando = null;

        public DbCommand Comando
        {
            get { return comando; }
        }
        private DbTransaction transaccion = null;

        public DbTransaction Transaccion
        {
            get { return transaccion; }
        }
        private string cadenaConexion;

        public string CadenaConexion
        {
            get { return cadenaConexion; }
            set { cadenaConexion = value; }
        }

        private static DbProviderFactory factory = null;

        private ProveedorDatos proveedor;

        /// <summary>
        /// Crea una instancia del acceso a la base de datos.
        /// </summary>
        public Datos(ProveedorDatos proveedor)
        {
            this.proveedor = proveedor;
            Configurar();
        }

        /// <summary>
        /// Configura el acceso a la base de datos para su utilización.
        /// </summary>
        /// <exception cref="DatosException">Si existe un error al cargar la configuración.</exception>
        private void Configurar()
        {
            try
            {
                string proveedor = "";
                switch (this.proveedor)
                {
                    case ProveedorDatos.SqlServer:
                        proveedor = ConfigurationManager.AppSettings.Get("PROVEEDOR_ADONET");
                        this.cadenaConexion = ConfigurationManager.AppSettings.Get("CADENA_CONEXION");
                        break;
                    case ProveedorDatos.FoxDbf:
                        proveedor = ConfigurationManager.AppSettings.Get("PROVEEDOR_OLEDB");
                        this.cadenaConexion = ConfigurationManager.AppSettings.Get("CADENA_CONEXION_DBF");
                        break;
                }
                Datos.factory = DbProviderFactories.GetFactory(proveedor);
            }
            catch (ConfigurationException ex)
            {
                throw new DatosException("Error al cargar la configuración del acceso a datos.", ex);
            }
        }

        /// <summary>
        /// Permite desconectarse de la base de datos.
        /// </summary>
        public void Desconectar()
        {
            if (this.conexion.State.Equals(ConnectionState.Open))
            {
                this.conexion.Close();
            }
        }

        /// <summary>
        /// Se concecta con la base de datos.
        /// </summary>
        /// <exception cref="DatosException">Si existe un error al conectarse.</exception>
        public void Conectar()
        {
            if (this.conexion != null && !this.conexion.State.Equals(ConnectionState.Closed))
            {
                throw new DatosException("La conexión ya se encuentra abierta.");
            }
            try
            {
                if (this.conexion == null)
                {
                    this.conexion = factory.CreateConnection();
                    this.conexion.ConnectionString = cadenaConexion;
                }
                this.conexion.Open();
            }
            catch (SqlException ex)
            {
                throw new DatosException("Hubo un error en la instancia de SQL Server.", ex);
            }
            catch (DataException ex)
            {
                throw new DatosException("Error al conectarse a la base de datos.", ex);
            }
        }

        /// <summary>
        /// Aplica los roles de seguridad para el acceso a la base de datos
        /// </summary>
        public bool AplicarRoles(string roleApp, string roleAppPwd)
        {
            try
            {
                System.Data.SqlClient.SqlCommand commandoRoles = new System.Data.SqlClient.SqlCommand("EXEC sp_setapprole '" + roleApp + "', '" + roleAppPwd + "'", (System.Data.SqlClient.SqlConnection)conexion);
                int resultado = commandoRoles.ExecuteNonQuery();
                commandoRoles.Dispose();
                commandoRoles = null;
                if (resultado == -1)
                    return true;
                else
                    return false;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Crea un comando en base a una sentencia SQL.
        /// Ejemplo:
        /// <code>SELECT * FROM Tabla WHERE campo1=@campo1, campo2=@campo2</code>
        /// Guarda el comando para el seteo de parámetros y la posterior ejecución.
        /// </summary>
        /// <param name="sentenciaSQL">La sentencia SQL con el formato: SENTENCIA [param = @param,]</param>
        public void CrearComando(string sentenciaSQL)
        {
            if (this.comando != null)
            {
                this.comando.Dispose();
                this.comando = null;
            }
            this.comando = factory.CreateCommand();
            this.comando.Connection = this.conexion;
            this.comando.CommandType = CommandType.Text;
            this.comando.CommandText = sentenciaSQL;
            if (this.transaccion != null)
            {
                this.comando.Transaction = this.transaccion;
            }
        }

        public void CrearComandoStoredProcedure(string procedureName)
        {
            if (comando != null)
            {
                comando.Dispose();
                comando = null;
            }
            comando = factory.CreateCommand();
            comando.Connection = conexion;
            comando.CommandType = CommandType.StoredProcedure;
            comando.CommandText = procedureName;
        }

        public void AsignarParametro(string nombre, object valor, SqlDbType tipo)
        {
            var parameter = new SqlParameter(nombre, tipo)
            {
                Value = valor ?? DBNull.Value
            };
            comando.Parameters.Add(parameter);
        }

        /// <summary>
        /// Setea un parámetro como nulo del comando creado.
        /// </summary>
        /// <param name="nombre">El nombre del parámetro cuyo valor será nulo.</param>
        public void AsignarParametroNulo(string nombre)
        {
            AsignarParametro(nombre, "", "NULL");
        }

        /// <summary>
        /// Asigna un parámetro de tipo cadena al comando creado.
        /// </summary>
        /// <param name="nombre">El nombre del parámetro.</param>
        /// <param name="valor">El valor del parámetro.</param>
        public void AsignarParametroCadena(string nombre, string valor)
        {
            AsignarParametro(nombre, "'", valor.ToString());
        }

        /// <summary>
        /// Asigna un parámetro de tipo entero al comando creado.
        /// </summary>
        /// <param name="nombre">El nombre del parámetro.</param>
        /// <param name="valor">El valor del parámetro.</param>
        public void AsignarParametroEntero(string nombre, int valor)
        {
            AsignarParametro(nombre, "", valor.ToString());
        }

        /// <summary>
        /// Asigna un parámetro de tipo entero al comando creado.
        /// </summary>
        /// <param name="nombre">El nombre del parámetro.</param>
        /// <param name="valor">El valor del parámetro.</param>
        public void AsignarParametroEntero(string nombre, long valor)
        {
            AsignarParametro(nombre, "", valor.ToString());
        }

        /// <summary>
        /// Asigna un parámetro de tipo numerico al comando creado.
        /// </summary>
        /// <param name="nombre">El nombre del parámetro.</param>
        /// <param name="valor">El valor del parámetro.</param>
        public void AsignarParametroNumerico(string nombre, double valor)
        {
            AsignarParametro(nombre, "", valor.ToString().Replace(',', '.'));
        }

        /// <summary>
        /// Asigna un parámetro de tipo bit al comando creado.
        /// </summary>
        /// <param name="nombre">El nombre del parámetro.</param>
        /// <param name="valor">El valor del parámetro.</param>
        public void AsignarParametroBit(string nombre, bool valor)
        {
            if (valor)
                AsignarParametro(nombre, "", "1");
            else
                AsignarParametro(nombre, "", "0");
        }

        ///// <summary>
        ///// Asigna un parámetro de tipo numeric al comando creado.
        ///// </summary>
        ///// <param name="nombre">El nombre del parámetro.</param>
        ///// <param name="valor">El valor del parámetro.</param>
        //public void AsignarParametroNumerico(string nombre, double valor)
        //{
        //    AsignarParametro(nombre, "", valor.ToString());
        //}

        /// <summary>
        /// Asigna un parámetro al comando creado.
        /// </summary>
        /// <param name="nombre">El nombre del parámetro.</param>
        /// <param name="separador">El separador que será agregado al valor del parámetro.</param>
        /// <param name="valor">El valor del parámetro.</param>
        private void AsignarParametro(string nombre, string separador, string valor)
        {
            int indice = this.comando.CommandText.IndexOf(nombre);
            string prefijo = this.comando.CommandText.Substring(0, indice);
            string sufijo = this.comando.CommandText.Substring(indice + nombre.Length);
            this.comando.CommandText = prefijo + separador + valor + separador + sufijo;
        }

        /// <summary>
        /// Asigna un parámetro de tipo fecha al comando.
        /// </summary>
        /// <param name="nombre">El nombre del parámetro.</param>
        /// <param name="valor">El valor del parámetro.</param>
        public void AsignarParametroFecha(string nombre, DateTime valor)
        {
            var parameter = new SqlParameter(nombre, SqlDbType.DateTime)
            {
                Value = valor
            };
            comando.Parameters.Add(parameter);
            //AsignarParametro(nombre, "'", valor.ToString("yyyyMMdd HH:mm:ss"));
        }

        /// <summary>
        /// Asigna un parámetro de tipo fecha al comando.
        /// </summary>
        /// <param name="nombre">El nombre del parámetro.</param>
        /// <param name="valor">El valor del parámetro.</param>
        public void AsignarParametroFechaCorta(string nombre, DateTime valor)
        {
            var parameter = new SqlParameter(nombre, SqlDbType.DateTime)
            {
                Value = valor
            };
            comando.Parameters.Add(parameter);
            //AsignarParametro(nombre, "'", valor.ToString("yyyyMMdd"));
        }

        /// <summary>
        /// Ejecuta el comando creado y retorna el resultado de la consulta.
        /// </summary>
        /// <returns>El resultado de la consulta.</returns>
        /// <exception cref="DatosException">Si ocurre un error al ejecutar el comando.</exception>
        public DbDataReader EjecutarConsulta()
        {
            this.comando.CommandTimeout = 120;
            return this.comando.ExecuteReader();
        }

        /// <summary>
        /// Ejecuta el comando creado y retorna el resultado de la consulta en un DbDataAdapter.
        /// </summary>
        /// <returns>El resultado de la consulta.</returns>
        /// <exception cref="DatosException">Si ocurre un error al ejecutar el comando.</exception>
        public DbDataAdapter EjecutarConsultaDS()
        {
            DbDataAdapter adaptador = factory.CreateDataAdapter();
            adaptador.SelectCommand = comando;
            return adaptador;
        }

        /// <summary>
        /// Ejecuta el comando creado y retorna un escalar.
        /// </summary>
        /// <returns>El escalar que es el resultado del comando.</returns>
        /// <exception cref="DatosException">Si ocurre un error al ejecutar el comando.</exception>
        public int EjecutarEscalar()
        {
            int escalar = 0;
            try
            {
                object exit = this.comando.ExecuteScalar();
                if (exit != null)
                {
                    int.TryParse(exit.ToString(), out escalar);
                }
            }
            catch (InvalidCastException ex)
            {
                throw new DatosException("Error al ejecutar un escalar.", ex);
            }
            return escalar;

        }


        public T EjecutarEscalar<T>()
        {
            try
            {
                object exit = this.comando.ExecuteScalar();
                Type t = typeof(T);
                t = Nullable.GetUnderlyingType(t) ?? t;

                return (exit == null || DBNull.Value.Equals(exit)) ?
                   default(T) : (T)Convert.ChangeType(exit, t);
            }
            catch (InvalidCastException ex)
            {
                throw new DatosException("Error al ejecutar un escalar.", ex);
            }
        }

        public T EjecutarValorStoredProcedure<T>(string returnName, SqlDbType returnType)
        {
            var parameter = new SqlParameter(returnName, returnType)
            {
                Direction = ParameterDirection.ReturnValue
            };
            comando.Parameters.Add(parameter);
            comando.ExecuteNonQuery();
            object value = comando.Parameters[returnName].Value;
            return (T) value;
        }

        /// <summary>
        /// Ejecuta el comando creado.
        /// </summary>
        public void EjecutarComando()
        {
            this.comando.ExecuteNonQuery();
        }

        /// <summary>
        /// Ejecuta el comando creado.
        /// </summary>
        public int EjecutarComandoInt()
        {
            return this.comando.ExecuteNonQuery();
        }


        /// <summary>
        /// Comienza una transacción en base a la conexion abierta.
        /// Todo lo que se ejecute luego de esta ionvocación estará 
        /// dentro de una tranasacción.
        /// </summary>
        public void ComenzarTransaccion()
        {
            if (this.transaccion == null)
            {
                this.transaccion = this.conexion.BeginTransaction(IsolationLevel.Serializable);
            }
        }

        /// <summary>
        /// Cancela la ejecución de una transacción.
        /// Todo lo ejecutado entre ésta invocación y su 
        /// correspondiente <c>ComenzarTransaccion</c> será perdido.
        /// </summary>
        public void CancelarTransaccion()
        {
            if (this.transaccion != null)
            {
                this.transaccion.Rollback();
            }
            this.transaccion.Dispose();
            this.transaccion = null;
        }

        /// <summary>
        /// Confirma todo los comandos ejecutados entre el <c>ComanzarTransaccion</c>
        /// y ésta invocación.
        /// </summary>
        public void ConfirmarTransaccion()
        {
            if (this.transaccion != null)
            {
                this.transaccion.Commit();
            }
            this.transaccion.Dispose();
            this.transaccion = null;
        }

    }

Edit : I forgot to say that the Connection is created in the Main method of Program.cs of the main form like this:

Datos datos = new Datos(ProveedorDatos.SqlServer);
datos.Conectar();
Application.Run(new FrmApp(datos));
datos.Desconectar();

I think the problem is that you are creating a new connection every time a function calls factory.CreateConnection() so you are creating multiple connections. I dont see were are you disposing it (connection.Dispose()).

Try 2 things:

  • Use "using" keyword when you create a new connection, so you dont have to worry about disposing it.

  • Implement a singleton or multiton for de conection:

    Singleton is something like this:

     public class Singleton { private static Singleton theInstance; private Singleton(){ theInstance=null; } public static Singleton getInstance(){ if ( theInstance==null ) theInstance= new Singleton(); return theInstance; } } 

    You have an instance of conection class, when that instance is null you create a new instance, but when instance already exists you return that instance. This guarantees that only one connection would be created. There are many implementations of this, try doing it with a fixed number of connections, not for only one.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM