由于连接池不用于一般的数据连接,一旦发生连接泄漏应用程序就可能死锁或崩溃,所以我们应该慎重的处理。
也许不少人在ADO.NET下的编程模式还是这样的:
SqlConnection conn = new SqlConnection(conn); //执行Sql或Procedure conn.Close(); |
然而,这就象是公式定律一样的编撰方法,在假如你使用了connection pool的情况下,就变成了隐藏的杀手,因为谁也不能保证你的Sql或Procedure是否正确的执行了,或者这些代码发生一些意想不到的异常,假如这段代码在连接打开了,但没有关闭的情况下就引发了一个异常,但是负责编撰数据访问层的Client的程序员可能很聪明的捕获了该异常,并告知用户操作失败是否重试。应用程序暂时运行正常了,但连接泄漏了…..
当被泄漏的连接超时却仍未被释放或返回连接池,将再次抛出一个异常。
数据访问代码编撰者的任务之一就是杜绝该异常的抛出。
于是我又看到有的代码这样写,的确可以防止连接泄漏:
using (SqlConnection conn = new SqlConnection(connectionString)) { //执行Sql或Procedure return result; } |
我们可能会遇到两种情况,方法要么是返回数据,要么返回DataReader,但using语句块只支持返回数据。我们可以使用try….catch…finally语句。
前一种的解决方法除了使用using语句,还有就是在实现方法时就应该考虑最坏的情况,即代码执行失败,我们应该捕获该异常但不处理,当我们在数据层把清理工作做完,再把异常抛出让别人去处理
SqlConnection conn = new SqlConnection(connectionString); SqlCommand cmd = new SqlCommand(); try { //……………… return cmd.ExecuteNonQuery(); } finally { if (conn.State == ConnectionState.Open) conn.Dispose(); cmd.Connection = null; cmd.Parameters.Clear(); cmd.Dispose(); } } |
而对付DataReader,我们有更简单的办法,就是CommandBehavior.CloseConnection枚举。
SqlCommand cmd = new SqlCommand(); SqlConnection conn = new SqlConnection(connectionString); try { //..................... SqlDataReader rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection); cmd.Parameters.Clear(); return rdr; } catch { if (conn.State == ConnectionState.Close) conn.Dispose(); throw; } |
DataReader使用了CommandBehavior.CloseConnection枚举以后,就可以调用DataReader的Close()或Dispose()方法关闭数据连接。如果你把返回的DataReader绑定到DataGridView控件上,它会自动关闭连接。但如果绑定到简单控件上就必须显式的调用DataReader.Close()。