做这个项目有近三个月了,在测试网页时总是感觉速度挺慢,但是一直没有发现是哪里的问题。前几天做的一个比较复杂页面中,一个按钮就调用了几次数据访问层的方法,这样反复几次操作之后页面就会崩掉,返回的错误大致是:连接池中的连接已达到最大上限,不能在打开新的连接了。原来是连接池泄露了
我在数据库访问层中写的方法都是调用MS的SQLHelpe类中提供的方法来直接访问数据库的,其中的ExecuteReader方法如下:
{
SqlCommand cmd = new SqlCommand();
SqlConnection conn = new SqlConnection(connString);
// we use a try/catch here because if the method throws an exception we want to
// close the connection throw code, because no datareader will exist, hence the
// commandBehaviour.CloseConnection will not work
try
{
PrepareCommand(cmd, conn, null, cmdType, cmdText, cmdParms);
SqlDataReader rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
cmd.Parameters.Clear();
return rdr;
}
catch
{
conn.Close();
throw;
}
}
既然有CommandBehavior.CloseConnection了,我想读取完数据连接就应该自动关闭了吧,就没在我的代码里手动关闭,结果反而就是上了这个自动关闭的当了。在William Vaughnm的文章里,他说到了连接池泄露的几种可能性:
“当代码完成 DataReader 后,您要在 SqlConnection 对象停止作用之前关闭 SqlConnection。要处理行集,您可以将 DataReader 传递到应用程序中的另一个例程,但仍然需要确保 DataReader 及其连接处于关闭状态。如果您不关闭 SqlConnection,代码会“泄漏”每个操作的连接,于是连接池对连接进行累积,最后便发生溢出。与 ADO 和 Visual Basic (VB) 6.0 中的情况不同,.NET 垃圾回收器不会为您关闭 SqlConnection 并进行清理。
“您也可能在使用 DataAdapter 对象时遇到问题。DataAdapter Fill 和 Update 方法可自动打开 DataAdapter 对象的连接,并在数据 I/O 操作完成后关闭该连接。不过,如果该连接在执行 Fill 或 Update 方法时已经处于打开状态,那么,ADO.NET 在方法执行完以后不会关闭 SqlConnection。这是另一个发生连接“泄漏”的机会。”
既然CommandBehavior.CloseConnection不能够保证在使用完连接后关闭它,那么它有什么用呢?
“只有当您在 ASP.NET Web 应用程序中使用复杂的绑定控件时,该选项才以这种方式工作。在整个 DataReader 结果集中循环到其行集的末尾(也就是说,当 Dr.Read — DataReader 的 Read 方法 — 返回 False 时)还不足以触发连接的自动关闭。不过,如果您绑定到一个复杂的绑定控件(例如,DataGrid),该控件则会关闭 DataReader 和连接 ”
在每次使用完SQLHelper返回的DataReader对象之后加上一个手动关闭Reader的语句,在每次使用完SQLHelper的更新数据库操作之后都加上关闭数据库连接的语句,问题得到解决。