Lo siento por solo comentar en primer lugar, pero estoy publicando casi todos los días un comentario similar ya que muchas personas piensan que sería inteligente encapsular la funcionalidad de ADO.NET en una DB-Class (yo también hace 10 años). En su mayoría, deciden usar objetos estáticos / compartidos, ya que parece ser más rápido que crear un nuevo objeto para cualquier acción.
Esa no es una buena idea en términos de rendimiento ni en términos de seguridad contra fallas.
No caza furtivamente en el territorio de Connection-Pool
Hay una buena razón por la que ADO.NET administra internamente las conexiones subyacentes al DBMS en el grupo de conexiones de ADO-NET :
En la práctica, la mayoría de las aplicaciones utilizan solo una o algunas configuraciones diferentes para las conexiones. Esto significa que durante la ejecución de la aplicación, muchas conexiones idénticas se abrirán y cerrarán repetidamente. Para minimizar el costo de abrir conexiones, ADO.NET utiliza una técnica de optimización llamada agrupación de conexiones.
La agrupación de conexiones reduce el número de veces que se deben abrir nuevas conexiones. El agrupador mantiene la propiedad de la conexión física. Gestiona las conexiones manteniendo vivo un conjunto de conexiones activas para cada configuración de conexión dada. Siempre que un usuario llama a Open en una conexión, el agrupador busca una conexión disponible en el grupo. Si hay una conexión agrupada disponible, se la devuelve a la persona que llama en lugar de abrir una nueva conexión. Cuando la aplicación llama a Cerrar en la conexión, el agrupador la devuelve al conjunto agrupado de conexiones activas en lugar de cerrarlo. Una vez que la conexión se devuelve al grupo, está lista para ser reutilizada en la próxima llamada abierta.
Entonces, obviamente, no hay razón para evitar crear, abrir o cerrar conexiones, ya que en realidad no se crean, abren ni cierran en absoluto. Esta es "solo" una bandera para que el grupo de conexiones sepa cuándo una conexión se puede reutilizar o no. Pero es una bandera muy importante, porque si una conexión está "en uso" (asume el grupo de conexiones), se debe abrir una nueva conexión física al DBMS, lo que es muy costoso.
Por lo tanto, no obtiene ninguna mejora en el rendimiento, sino todo lo contrario. Si se alcanza el tamaño máximo de grupo especificado (100 es el predeterminado), incluso obtendría excepciones (demasiadas conexiones abiertas ...). Por lo tanto, esto no solo afectará enormemente al rendimiento, sino que también será una fuente de errores desagradables y (sin usar Transacciones) un área de descarga de datos.
Si incluso está utilizando conexiones estáticas, está creando un bloqueo para cada hilo que intente acceder a este objeto. ASP.NET es un entorno de subprocesos múltiples por naturaleza. Así que hay una gran posibilidad de que estos bloqueos provoquen problemas de rendimiento en el mejor de los casos. En realidad, tarde o temprano obtendrá muchas excepciones diferentes (como que su ExecuteReader requiere una conexión abierta y disponible ).
Conclusión :
- No reutilice conexiones ni ningún objeto ADO.NET en absoluto.
- No los haga estáticos / compartidos (en VB.NET)
- Siempre cree, abra (en caso de conexiones), úselos, ciérrelos y deséchelos donde los necesite (por ejemplo, en un método)
- utilice
using-statement
para eliminar y cerrar (en caso de conexiones) implícitamente
Eso es cierto no solo para Connections (aunque es más notable). Cada objeto que se implemente IDisposable
debe eliminarse (más simple por using-statement
), más aún en el System.Data.SqlClient
espacio de nombres.
Todo lo anterior habla en contra de una DB-Class personalizada que encapsula y reutiliza todos los objetos. Esa es la razón por la que comenté que lo tirara a la basura. Esa es solo una fuente de problemas.
Editar : Aquí hay una posible implementación de su retrievePromotion
método:
public Promotion retrievePromotion(int promotionID)
{
Promotion promo = null;
var connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["MainConnStr"].ConnectionString;
using (SqlConnection connection = new SqlConnection(connectionString))
{
var queryString = "SELECT PromotionID, PromotionTitle, PromotionURL FROM Promotion WHERE PromotionID=@PromotionID";
using (var da = new SqlDataAdapter(queryString, connection))
{
// you could also use a SqlDataReader instead
// note that a DataTable does not need to be disposed since it does not implement IDisposable
var tblPromotion = new DataTable();
// avoid SQL-Injection
da.SelectCommand.Parameters.Add("@PromotionID", SqlDbType.Int);
da.SelectCommand.Parameters["@PromotionID"].Value = promotionID;
try
{
connection.Open(); // not necessarily needed in this case because DataAdapter.Fill does it otherwise
da.Fill(tblPromotion);
if (tblPromotion.Rows.Count != 0)
{
var promoRow = tblPromotion.Rows[0];
promo = new Promotion()
{
promotionID = promotionID,
promotionTitle = promoRow.Field<String>("PromotionTitle"),
promotionUrl = promoRow.Field<String>("PromotionURL")
};
}
}
catch (Exception ex)
{
// log this exception or throw it up the StackTrace
// we do not need a finally-block to close the connection since it will be closed implicitely in an using-statement
throw;
}
}
}
return promo;
}