相信使用過ADO.NET的同志多半都見過這個exception吧:
There is already an open DataReader associated with this Command which must be closed first.
拋出這個exception的主要原因是:一個SqlConnection只能和一個開著的SqlDataReader相關聯。當開 發人員忘記關掉打開的SqlDataReader,而又嘗試打開一個新的SqlDataReader的時候,BCL就會拋出上述 異常。重現方法如下:
SqlConnection conn = new SqlConnection(connStr);
conn.Open();
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "SELECT * FROM Person";
SqlDataReader reader1 = cmd.ExecuteReader();
while (reader1.Read())
{ /* Read Fields */ }
// Forget to close the DataReader
cmd.CommandText = "SELECT * FROM Course";
SqlDataReader reader2 = cmd.ExecuteReader(); // Throws the above exception
while (reader2.Read())
{ /* Read Fields */ }
這段代碼很簡單,我們一眼就能看出那個罪惡的DataReader。但是在實際的開發環境中,代碼的封裝 會造成當 SqlDataReader reader2 = cmd.ExecuteReader(); 拋出exception時,我們很難找到那個忘關 的DataReader。
下面,我將介紹一種方法,從SqlConnection回溯到打開著的SqlDataReader。
SqlConnection自身是沒有任何public的方法可以返回當前打開著的SqlDataReader的,但是 SqlConnection卻有一些internal method能幫助我們得到所要的信息。它們是:
SqlConnection SqlInternalConnection GetOpenConnection(); SqlInternalConnection SqlDataReader FindLiveReader(SqlCommand command); SqlDataReader SqlCommand Command { get; }