Desculpe por apenas comentar em primeiro lugar, mas estou postando quase todos os dias um comentário semelhante, já que muitas pessoas pensam que seria inteligente encapsular a funcionalidade ADO.NET em uma classe de banco de dados (eu também há 10 anos). Principalmente, eles decidem usar objetos estáticos / compartilhados, pois parece ser mais rápido do que criar um novo objeto para qualquer ação.
Isso não é uma boa ideia em termos de desempenho nem em termos de segurança contra falhas.
Não roube o território do Connection-Pool
Há um bom motivo pelo qual o ADO.NET gerencia internamente as conexões subjacentes ao DBMS no pool de conexões ADO-NET :
Na prática, a maioria dos aplicativos usa apenas uma ou algumas configurações diferentes para conexões. Isso significa que durante a execução do aplicativo, muitas conexões idênticas serão abertas e fechadas repetidamente. Para minimizar o custo de abertura de conexões, o ADO.NET usa uma técnica de otimização chamada pooling de conexões.
O pool de conexão reduz o número de vezes que novas conexões devem ser abertas. O pooler mantém a propriedade da conexão física. Ele gerencia conexões mantendo ativo um conjunto de conexões ativas para cada configuração de conexão fornecida. Sempre que um usuário chama Open em uma conexão, o pooler procura uma conexão disponível no pool. Se uma conexão agrupada estiver disponível, ele a retornará ao chamador em vez de abrir uma nova conexão. Quando o aplicativo chama Close na conexão, o pooler o retorna para o conjunto agrupado de conexões ativas em vez de fechá-lo. Depois que a conexão é retornada ao pool, ela está pronta para ser reutilizada na próxima chamada aberta.
Obviamente, não há razão para evitar a criação, abertura ou fechamento de conexões, pois, na verdade, elas não são criadas, abertas e fechadas. Este é "apenas" um sinalizador para o pool de conexão saber quando uma conexão pode ser reutilizada ou não. Mas é um sinalizador muito importante, porque se uma conexão está "em uso" (o pool de conexão assume), uma nova conexão física deve ser aberta para o DBMS, o que é muito caro.
Portanto, você não está obtendo melhoria de desempenho, mas o oposto. Se o tamanho máximo do pool especificado (100 é o padrão) for atingido, você poderá até obter exceções (muitas conexões abertas ...). Portanto, isso não só afetará tremendamente o desempenho, mas também será uma fonte de erros desagradáveis e (sem usar transações) uma área de despejo de dados.
Se você estiver usando conexões estáticas, estará criando um bloqueio para cada thread que tentar acessar este objeto. ASP.NET é um ambiente multithreading por natureza. Portanto, há uma grande chance para esses bloqueios, o que causa, na melhor das hipóteses, problemas de desempenho. Na verdade, mais cedo ou mais tarde você terá muitas exceções diferentes (como o ExecuteReader requer uma conexão aberta e disponível ).
Conclusão :
- Não reutilize conexões ou quaisquer objetos ADO.NET.
- Não os torne estáticos / compartilhados (em VB.NET)
- Sempre crie, abra (no caso de conexões), use, feche e descarte-os onde você precisar deles (por exemplo, em um método)
- use o
using-statement
para descartar e fechar (no caso de conexões) implicitamente
Isso é verdade não apenas para Conexões (embora seja mais perceptível). Cada objeto implementado IDisposable
deve ser descartado (mais simples por using-statement
), ainda mais no System.Data.SqlClient
namespace.
Tudo o que foi dito acima fala contra uma classe de banco de dados personalizada que encapsula e reutiliza todos os objetos. Essa é a razão pela qual eu comentei para o lixo. Essa é apenas uma fonte de problema.
Edit : Aqui está uma possível implementação de seu 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;
}