ExecuteReader需要一个开放和可用的连接。 连接的当前状态是正在连接

当尝试通过ASP.NET在线连接到MSSQL数据库时,当两个或更多人同时连接时,我将得到以下内容:

ExecuteReader需要一个开放和可用的连接。 连接的当前状态是正在连接。

该网站在我的本地主机服务器上正常工作。

这是粗略的代码。

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; } 

我可以知道可能发生了什么问题,我该如何解决?

编辑:不要忘记,我的连接string和连接都是静态的。 我相信这是原因。 请指教。

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

对不起,只有评论首先,但我几乎每天都发布类似的评论,因为许多人认为将ADO.NETfunction封装到一个DB级(十年前我也是)是聪明的。 大多数情况下,他们决定使用静态/共享对象,因为它似乎比为任何操作创build新对象要快。

这在性能和故障安全方面既不是好主意。

不要在连接池的领土上偷猎

ADO.NET内部pipe理ADO-NET连接池中 DBMS的底层连接有一个很好的理由:

实际上,大多数应用程序只使用一个或几个不同的连接configuration。 这意味着在应用程序执行过程中,许多相同的连接将被重复打开和closures。 为了最小化打开连接的成本,ADO.NET使用一种称为连接池的优化技术。

连接池减less了必须打开新连接的次数。 这个池子保持物理连接的所有权。 它通过为每个给定的连接configuration保持一组活动连接来pipe理连接。 每当用户在连接上调用Open时,池就会在池中查找可用的连接。 如果有一个池连接可用,它将它返回给调用者,而不是打开一个新的连接。 当应用程序在连接上调用Close时,调度程序将其返回到活动连接池集合,而不是closures它。 一旦连接返回到池中,就可以在下次打开呼叫时重新使用该连接。

所以显然没有理由避免创build,打开或closures连接,因为实际上它们并不是创build,打开和closures的。 这是“连接池”的唯一标识,以便知道连接何时可以重用。 但是这是一个非常重要的标志,因为如果连接处于“正在使用中”(连接池假定),则必须将一个新的物理连接打开到DBMS,这非常昂贵。

所以你没有获得性能改善,反而相反。 如果指定的最大池大小(默认为100),则甚至会发生exception(打开的连接太多…)。 所以这不仅会极大地影响性能,而且会成为恶意错误和(不使用交易)数据倾销区域的根源。

如果你甚至使用静态连接,你将为每个试图访问这个对象的线程创build一个锁。 ASP.NET本质上是一个multithreading环境。 因此,这些locking很有可能导致性能问题。 实际上迟早你会得到许多不同的exception(如你的ExecuteReader需要一个开放和可用的连接 )。

结论

  • 不要重用连接或任何ADO.NET对象。
  • 不要让他们静态/共享(在VB.NET中)
  • 总是创build,打开(在连接的情况下),使用,closures并放置在你需要的地方(在一个方法中)
  • 使用using-statement隐式地处理和closures(在Connections的情况下)

这不仅适用于连接(尽pipe最为明显)。 实现IDisposable每个对象都应该被放置(最简单的是using-statement ),而在System.Data.SqlClient命名空间中System.Data.SqlClient

以上所有内容都是针对封装和重用所有对象的自定义DB-Class。 这就是为什么我评论去垃圾的原因。 这只是一个问题的来源。


编辑 :这是一个可能的实现你的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<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; }