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.