ExecuteReader requer uma conexão aberta e disponível. O estado atual da conexão é Conectando

Ao tentar se conectar ao database MSSQL via ASP.NET on-line, receberei o seguinte quando duas ou mais pessoas se conectarem simultaneamente:

ExecuteReader requer uma conexão aberta e disponível. O estado atual da conexão é Conectando.

O site funciona bem no meu servidor localhost.

Este é o código aproximado.

public Promotion retrievePromotion() { int promotionID = 0; string promotionTitle = ""; string promotionUrl = ""; Promotion promotion = null; SqlOpenConnection(); SqlCommand sql = SqlCommandConnection(); sql.CommandText = "SELECT TOP 1 PromotionID, PromotionTitle, PromotionURL FROM Promotion"; SqlDataReader dr = sql.ExecuteReader(); while (dr.Read()) { promotionID = DB2int(dr["PromotionID"]); promotionTitle = DB2string(dr["PromotionTitle"]); promotionUrl = DB2string(dr["PromotionURL"]); promotion = new Promotion(promotionID, promotionTitle, promotionUrl); } dr.Dispose(); sql.Dispose(); CloseConnection(); return promotion; } 

Posso saber o que pode ter corrido mal e como corrigi-lo?

Edit: Para não esquecer, minha string de conexão e conexão estão em estática. Eu acredito que esta é a razão. Por favor informar.

 public static string conString = ConfigurationManager.ConnectionStrings["dbConnection"].ConnectionString; public static SqlConnection conn = null; 

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 do ADO.NET em um DB-Class (eu também 10 anos atrás). Principalmente, eles decidem usar objects estáticos / compartilhados, pois parece ser mais rápido do que criar um novo object 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 Pool de Conexão

Há uma boa razão pela qual o ADO.NET gerencia internamente as Conexões subjacentes ao DBMS no ADO-NET Connection-Pool :

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 pool de conexão.

O pool de conexões reduz o número de vezes que novas conexões devem ser abertas. O parceiro mantém a propriedade da conexão física. Ele gerencia as conexões mantendo vivo um conjunto de conexões ativas para cada configuração de conexão. 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 em pool estiver disponível, ela 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 em pool de conexões ativas, em vez de fechá-lo. Depois que a conexão for retornada ao pool, ela estará pronta para ser reutilizada na próxima chamada Open.

Então, obviamente, não há razão para evitar criar, abrir ou fechar conexões, já que elas não são criadas, abertas e fechadas. Isso é “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ões assume), uma nova conexão física deve ser aberta para o DBMS, o que é muito caro.

Então você não está obtendo melhoria de desempenho, mas o contrário. Se o tamanho máximo do conjunto especificado (100 é o padrão) for atingido, você 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 as 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 esse object. O ASP.NET é um ambiente multithreading por natureza. Portanto, há uma grande chance para esses bloqueios que, na melhor das hipóteses, causam problemas de desempenho. Na verdade, mais cedo ou mais tarde, você terá muitas exceções diferentes (como o seu ExecuteReader requer uma conexão aberta e disponível ).

Conclusão :

  • Não reutilize conexões ou quaisquer objects do ADO.NET.
  • Não os torne estáticos / compartilhados (em VB.NET)
  • Sempre crie, abra (no caso do Connections), use, feche e descarte onde você precisa (fe em um método)
  • use a using-statement para descartar e fechar (no caso do Connections) implicitamente

Isso é verdade não apenas para o Connections (embora seja mais perceptível). Todos os objects que implementam IDisposable devem ser descartados (mais simples using-statement ), tudo isso no namespace System.Data.SqlClient .

Todos os itens acima falam contra uma class de database personalizada que encapsula e reutiliza todos os objects. Essa é a razão pela qual eu comentei o lixo. Isso é apenas uma fonte de problemas.


Edit : Aqui está uma possível implementação do seu método retrievePromotion :

 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("PromotionTitle"), promotionUrl = promoRow.Field("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; }