ExecuteReader 需要打开且可用的 Connection。连接的当前状态为 Connection

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

ExecuteReader 需要打开且可用的 Connection。连接的当前状态为 Connection。

该站点在我的本地主机服务器上运行良好。

这是粗略代码。

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;
197192 次浏览

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

就性能和故障安全而言,这都不是一个好主意。

不要在 Connection-Pool 的领土上偷猎

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

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

连接池减少了新连接的次数 必须打开。池保持物理的所有权 它通过保持一组活动的连接来管理连接 每个给定的连接配置的连接 在连接上调用 Open 时,池程序查找可用的 如果池连接可用,则 将其返回给调用方,而不是打开新连接 应用程序在连接上调用 Close,池将其返回给 而不是关闭活动连接的池集 连接返回到池中,就可以在 下一个公开电话。

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

所以你没有得到任何性能的提高,而是得到了相反的结果。如果达到指定的最大池大小(默认为100) ,甚至会出现异常(打开的连接太多...)。因此,这不仅会极大地影响性能,而且还会导致严重错误,并且(在不使用事务的情况下)成为数据转储区域。

如果您甚至使用静态连接,那么您就是在为每个试图访问该对象的线程创建一个锁。NET 本质上是一个多线程环境。因此,这些锁极有可能导致性能问题。实际上,你迟早会得到许多不同的异常(比如你的 ExecuteReader 需要打开且可用的 Connection)。

结论 :

  • 根本不要重用连接或任何 ADO.NET 对象。
  • 不要让它们是静态的/共享的(在 VB.NET 中)
  • 始终创建、打开(在连接的情况下)、使用、关闭和处置它们(例如在方法中)
  • 使用 using-statement隐式地释放和关闭(在连接的情况下)

这不仅适用于连接(尽管最明显)。应该释放实现 IDisposable的每个对象(最简单的是 using-statement) ,更应该在 System.Data.SqlClient名称空间中释放。

所有这些都与自定义 DB-Class 相对立,后者封装并重用所有对象。这就是我评论它的原因。这只是问题的根源。


Edit : 这是 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 implicitly in an using-statement
throw;
}
}
}
return promo;
}

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

在我的情况下,这是因为我使用一个交易对辛格尔顿。

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

我的解决办法是:

public class DbHelper : DbHelperCore
{
public DbHelper()
{
Connection = null;
Transaction = null;
}


public static DbHelper instance
{
get
{
if (HttpContext.Current is null)
return new DbHelper();
else if (HttpContext.Current.Items["dbh"] == null)
HttpContext.Current.Items["dbh"] = new DbHelper();


return (DbHelper)HttpContext.Current.Items["dbh"];
}
}


public override void BeginTransaction()
{
Connection = new SqlConnection(Entity.Connection.getCon);
if (Connection.State == System.Data.ConnectionState.Closed)
Connection.Open();
Transaction = Connection.BeginTransaction();
}
}

我为我的实例使用了 HttpContext.Current.Items。这个类 DbHelper 和 DbHelperCore 是我自己的类