当尝试通过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;
答案 0 :(得分:206)
很抱歉首先只发表评论,但我几乎每天都发布类似评论,因为很多人都认为将ADO.NET功能封装到DB-Class中是很明智的(我也是10年前) 。大多数情况下,他们决定使用静态/共享对象,因为它似乎比为任何操作创建新对象更快。
在表现方面和失败安全方面,这既不是一个好主意。
ADO.NET在ADO-NET Connection-Pool内部管理DBMS的基础连接是有充分理由的:
实际上,大多数应用程序只使用一种或几种不同的应用程序 连接配置。这意味着在申请期间 执行时,将重复打开许多相同的连接 关闭。为了最大限度地降低打开连接的成本,ADO.NET使用了 优化技术称为连接池。
连接池减少了新连接的次数 必须打开。平民保持对物质的所有权 连接。它通过保持一组活动来管理连接 每个给定连接配置的连接。每当一个用户 调用在连接上打开,pooler查找可用的 池中的连接。如果池连接可用,则为 将其返回给调用者而不是打开新连接。当。。。的时候 应用程序调用关闭连接,pooler将其返回 合并的一组活动连接而不是关闭它。一旦 连接返回到池中,它已准备好在池上重用 下一次公开电话。
所以显然没有理由避免创建,打开或关闭连接,因为实际上它们根本没有创建,打开和关闭。这是“仅”连接池的标志,以便知道何时可以重用连接。但它是一个非常重要的标志,因为如果连接“正在使用”(连接池假设),则必须向DBMS开放一个新的物理连接,这非常昂贵。
所以你没有获得任何性能提升,但相反。如果达到指定的最大池大小(默认值为100),您甚至会获得异常(打开的连接太多......)。因此,这不仅会极大地影响性能,还会成为令人讨厌的错误和(不使用事务)数据转储区域的来源。
如果您甚至使用静态连接,则会为尝试访问此对象的每个线程创建一个锁。 ASP.NET本质上是一个多线程环境。所以这些锁具很有可能造成性能问题。实际上迟早你会得到许多不同的例外(例如你的 ExecuteReader需要一个开放的可用连接)。
结论:
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是我自己的课程