ExecuteReader需要一个开放且可用的连接。连接的当前状态为“正在连接”

时间:2012-03-14 16:09:07

标签: c# .net sql-server ado.net database-connection

当尝试通过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;
}

我可能知道可能出现的问题,我该如何解决?

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

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

2 个答案:

答案 0 :(得分:206)

很抱歉首先只发表评论,但我几乎每天都发布类似评论,因为很多人都认为将ADO.NET功能封装到DB-Class中是很明智的(我也是10年前) 。大多数情况下,他们决定使用静态/共享对象,因为它似乎比为任何操作创建新对象更快。

在表现方面和失败安全方面,这既不是一个好主意。

不要挖掘连接池的区域

ADO.NET在ADO-NET Connection-Pool内部管理DBMS的基础连接是有充分理由的:

  

实际上,大多数应用程序只使用一种或几种不同的应用程序   连接配置。这意味着在申请期间   执行时,将重复打开许多相同的连接   关闭。为了最大限度地降低打开连接的成本,ADO.NET使用了   优化技术称为连接池。

     

连接池减少了新连接的次数   必须打开。平民保持对物质的所有权   连接。它通过保持一组活动来管理连接   每个给定连接配置的连接。每当一个用户   调用在连接上打开,pooler查找可用的   池中的连接。如果池连接可用,则为   将其返回给调用者而不是打开新连接。当。。。的时候   应用程序调用关闭连接,pooler将其返回   合并的一组活动连接而不是关闭它。一旦   连接返回到池中,它已准备好在池上重用   下一次公开电话。

所以显然没有理由避免创建,打开或关闭连接,因为实际上它们根本没有创建,打开和关闭。这是“仅”连接池的标志,以便知道何时可以重用连接。但它是一个非常重要的标志,因为如果连接“正在使用”(连接池假设),则必须向DBMS开放一个新的物理连接,这非常昂贵。

所以你没有获得任何性能提升,但相反。如果达到指定的最大池大小(默认值为100),您甚至会获得异常(打开的连接太多......)。因此,这不仅会极大地影响性能,还会成为令人讨厌的错误和(不使用事务)数据转储区域的来源。

如果您甚至使用静态连接,则会为尝试访问此对象的每个线程创建一个锁。 ASP.NET本质上是一个多线程环境。所以这些锁具很有可能造成性能问题。实际上迟早你会得到许多不同的例外(例如你的 ExecuteReader需要一个开放的可用连接)。

结论

  • 根本不重用连接或任何ADO.NET对象。
  • 不要让它们静态/共享(在VB.NET中)
  • 始终创建,打开(如果是连接),使用,关闭并将它们放置在您需要的地方(例如方法中)
  • 使用using-statement隐含地处理和关闭(如果是连接)

这不仅适用于Connections(尽管最值得注意)。应该处理实现IDisposable的每个对象(最简单的using-statement),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;
}

答案 1 :(得分:0)

几天前我发现了这个错误。

在我的情况下,这是因为我在Singleton上使用了Transaction。

.Net不能与如上所述的Singleton一起很好地工作。

我的解决方法是这样:

$domainnames = array();  // list of expiring URLs
$domaindate = array();  // list of expiry dates
while ($row = mysqli_fetch_array($query)) {

  $domainnames[] = $row['domainurl']; // add to list
  $domaindate[] = $row['expirationdate']; // add to list
} // that's it for the loop

我将HttpContext.Current.Items用于实例。此类DbHelper和DbHelperCore是我自己的课程