请在回复之前阅读整个问题。我道歉,我似乎永远不会写简短的问题......
我正在支持一个C#内部网络应用程序,该应用程序可以访问在Windows Small Business Server 2011 SP1上运行的SQL Server 2008 R2。
我们最近收到了很多SQL超时,这是一个例外情况:
System.Web.HttpUnhandledException:类型' System.Web.HttpUnhandledException'的异常。被扔了。 ---> System.InvalidOperationException:超时已过期。从池中获取连接之前经过的超时时间。这可能是因为所有池连接都在使用中并且达到了最大池大小。 在System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection) 在System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection,DbConnectionFactory connectionFactory) 在System.Data.SqlClient.SqlConnection.Open()
我检查了一些事情,其中一个是代码处理连接和关闭连接的方式。我已经在其他线程中读到,使用带有连接的Using语句就足够了,因为它... ...最后将连接创建包装起来并最终将连接处理调用放在finally"中。即使发生异常,连接也会关闭。
所以,我同意并且已经使用了这种方法多年。其他人建议显式关闭连接,即使在连接中使用Using语句也是如此。我认为这将是多余的......
然而,我的问题是关于命令对象。其他人为这个应用程序编写了一个大型的db方法库,他们(在所有的db方法中)使用语句在SqlConnection对象之前声明了SqlCommand对象。他们还在连接using语句之前将连接对象分配给命令对象。
更好的做法是在连接using语句中声明和使用命令对象,并且可以通过其他方式执行此操作导致sql连接超时(除了sql连接超时的其他原因)?以此代码为例:
public Musician GetMusician(int recordId)
{
Musician objMusician = null;
SqlConnection con = new SqlConnection(_connectionString);
SqlCommand cmd = new SqlCommand();
cmd.Connection = con;
cmd.CommandText = "selectMusician";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@id", recordId);
using (con)
{
con.Open();
SqlDataReader reader = cmd.ExecuteReader();
if (reader.HasRows)
{
reader.Read();
objMusician = new Musician((int)reader["id"]);
objMusician.Name = (string)reader["name"];
}
}
if objMusician != null)
{
objMusician.Albums = Albums.GetAlbums((int)objMusician.ID);
objMusician.Tours = Tours.GetTours((int)objMusician.ID);
objMusician.Interviews = Interviews.GetInterviews((int)objMusician.ID);
}
return objMusician;
}
还要知道调用页面已经尝试捕获它们,并且它是将错误记录到我们的日志记录数据库的页面。我们让异常冒泡到页面上的调用方法,然后在那里处理。
答案 0 :(得分:0)
完成后,您应该明确关闭连接。您永远不会关闭任何连接,因此在您达到连接池限制之后,您将获得错误,直到您手动回收池或它自行循环。在使用块内移动属性赋值块并执行con.Close(); cmd.Dispose();在回到你的objMusician之前:
using (con)
{
con.Open();
SqlDataReader reader = cmd.ExecuteReader();
if (reader.HasRows)
{
reader.Read();
objMusician = new Musician((int)reader["id"]);
objMusician.Name = (string)reader["name"];
}
if objMusician != null)
{
objMusician.Albums = Albums.GetAlbums((int)objMusician.ID);
objMusician.Tours = Tours.GetTours((int)objMusician.ID);
objMusician.Interviews = Interviews.GetInterviews((int)objMusician.ID);
}
con.Close();
cmd.Dispose();
return objMusician;
}
答案 1 :(得分:0)
不知道它是否有助于解决您的超时问题,但我总是按照以下方式构建我的代码并且没有遇到此问题:
using(var cmd = new SqlCommand())
{
using(var con = new SqlConnection(ConnectionString))
{
con.Open();
cmd.Connection = con;
cmd.CommandText = "selectMusician";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@id", recordId);
...
}
}
只是在MSDN上阅读,它说"当您使用完组件后调用Dispose。 Dispose方法使Component处于不可用状态。在调用Dispose之后,必须释放对Component的所有引用,以便垃圾收集器可以回收Component占用的内存。"这意味着为了让GC立即收集连接,您必须在处理命令之前处置连接,否则连接会一直挂起,直到GC到处调用Finalize为止。
答案 2 :(得分:0)
按如下方式重构您的方法。您可能遇到数据阅读器引用连接但尚未处理的情况。
public Musician GetMusician(int recordId)
{
Musician objMusician = null;
using(SqlConnection con = new SqlConnection(_connectionString))
{
con.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = con;
cmd.CommandText = "selectMusician";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@id", recordId);
using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
if (reader.HasRows)
{
reader.Read();
objMusician = new Musician((int) reader["id"]);
objMusician.Name = (string) reader["name"];
}
if objMusician != null)
{
objMusician.Albums = Albums.GetAlbums((int)objMusician.ID);
objMusician.Tours = Tours.GetTours((int)objMusician.ID);
objMusician.Interviews = Interviews.GetInterviews((int)objMusician.ID);
}
}
}
return objMusician;
}
}