Guo*_*Lim 106 .net c# sql-server ado.net database-connection
当尝试通过ASP.NET在线连接到MSSQL数据库时,当两个或更多人同时连接时,我将得到以下信息:
ExecuteReader需要一个开放且可用的连接.连接的当前状态为"正在连接".
该站点在我的localhost服务器上正常工作.
这是粗略的代码.
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;
}
Run Code Online (Sandbox Code Playgroud)
我可能知道可能出了什么问题,我该如何解决?
编辑:不要忘记,我的连接字符串和连接都是静态的.我相信这就是原因.请指教.
public static string conString = ConfigurationManager.ConnectionStrings["dbConnection"].ConnectionString;
public static SqlConnection conn = null;
Run Code Online (Sandbox Code Playgroud)
Tim*_*ter 219
很抱歉只在第一时间发表评论,但我几乎每天发布一个类似的评论,因为很多人认为将ADO.NET功能封装到DB-Class(我也是10年前)是明智的.大多数情况下,他们决定使用静态/共享对象,因为它似乎比为任何操作创建新对象更快.
从绩效和失败安全的角度来看,这既不是一个好主意.
ADO.NET内部管理ADO-NET连接池中 DBMS的基础连接有一个很好的理由:
实际上,大多数应用程序仅使用一种或几种不同的配置进行连接.这意味着在应用程序执行期间,将重复打开和关闭许多相同的连接.为了最小化打开连接的成本,ADO.NET使用称为连接池的优化技术.
连接池减少了必须打开新连接的次数.pooler保持物理连接的所有权.它通过为每个给定的连接配置保持一组活动连接来管理连接.每当用户在连接上调用Open时,pooler将在池中查找可用连接.如果池连接可用,则将其返回给调用者,而不是打开新连接.当应用程序在连接上调用Close时,pooler会将其返回到池化的活动连接集,而不是关闭它.一旦连接返回到池,就可以在下一个Open调用中重用它.
显然,没有理由避免创建,打开或关闭连接,因为实际上它们根本没有被创建,打开和关闭.这是"仅"连接池的标志,以便知道何时可以重用连接.但它是一个非常重要的标志,因为如果连接"正在使用"(连接池假设),则必须向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;
}
Run Code Online (Sandbox Code Playgroud)