问题描述
我是在写ASP.NET webform的时候遇到这个问题的。
在三层架构的DAL层有两个类,一个是FolderDal,一个是DBHelper。
想在FolderDal的GetGridViewData函数里面调用DBHelper的ExcuteReader函数,ExcuteReader函数会返回一个SqlDataReader,但是每次都会有这个错误。
后来打断点调试才发现了问题所在。
分析
下面来看看代码:
FolderDal.cs
public List<FolderInfo> GetGridViewData(int pID)
{
string cmdString = string.Format(@"SELECT
*
FROM
file_information
WHERE
pID = {0}
AND
file_type != 'folder'", pID);
DbHelper dbHelper = new DbHelper();
SqlDataReader reader = dbHelper.ExecuteReader(cmdString); // 执行数据库查询操作
List<FolderInfo> folderList = ConverToFolderList(reader);
return folderList;
}
private List<FolderInfo> ConverToFolderList(SqlDataReader reader)
{
List<FolderInfo> folderList = new List<FolderInfo>();
while (reader.Read())
{
FolderInfo folder = new FolderInfo();
folder.ID = reader.GetInt32(0);
folder.PID = reader.GetInt32(1);
folder.Name = reader.GetValue(2).ToString();
folder.Size = reader.GetInt64(3);
folder.CreateTime = reader.GetDateTime(4);
folder.ModifyTime = reader.GetDateTime(5);
folder.FileType = reader.GetValue(6).ToString();
folderList.Add(folder);
}
return folderList;
}
DBHelper.cs
public SqlDataReader ExecuteReader(string cmdStr)
{
using (SqlConnection conn = new SqlConnection(this.m_sqlConnectionString))
{
conn.Open();
SqlCommand cmd = CreateCMD(conn, cmdStr);
SqlDataReader reader = cmd.ExecuteReader();
return reader;
}
}
打断点时发现执行SqlDataReader reader = cmd.ExecuteReader();这一句之后reader确实是有内容的,然而再继续执行,返回到调用函数的时候reader里面的内容却没有了!
当时就猜会不会是跟数据库连接关闭了有关,于是改了一下代码,不用using,手动open和close来验证一下。
果然!在没有调用conn.close()是前reader一直还有内容,但是执行close之后reader就变为空了。
网上也搜到一些相关内容:
reader.read() 是要在连接状态 Connection 才可以用的
如果Connection 关闭了 就读取不了了
于是问题就可以解决了,我们在使用完reader时再关闭数据库连接就好。
可以使用 ExecuteReader(System.Data.CommandBehavior.CloseConnection)
来帮助我们完成这一点。
官方对于 CommandBehavior.CloseConnection
的解释:
在执行该命令时,如果关闭关联的 DataReader 对象,则关联的 Connection 对象也将关闭。
解决办法
直接看代码就好了
DBHelper
// 不用using而是自己来控制何时来open与close数据库连接
public SqlDataReader ExecuteReader(string cmdStr)
{
SqlConnection conn = new SqlConnection(this.m_sqlConnectionString);
conn.Open(); // 打开数据库连接,并且不再此函数里关闭
SqlCommand cmd = CreateCMD(conn, cmdStr);
return cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
}
FolderDal.ca
public List<FolderInfo> GetGridViewData(int pID)
{
string cmdString = string.Format(@"SELECT
*
FROM
file_information
WHERE
pID = {0}
AND
file_type != 'folder'", pID);
DbHelper dbHelper = new DbHelper();
SqlDataReader reader = dbHelper.ExecuteReader(cmdString);
List<FolderInfo> folderList = ConverToFolderList(reader);
reader.Close(); // 由于使用了CommandBehavior.CloseConnection,因此此时也会关闭相应的数据库连接
return folderList;
}
好了,问题解决了~~
不过其实不推荐这么做,因为数据库连接还是谁打开谁关闭比较安全,而在返回调用函数里再关闭有可能会忘记,这样就会出问题了。更好的办法其实是先把reader里的内容转换为DataTable,然后返回DataTable。这样的话即使数据库连接已经关闭,DataTable里面的数据还是在的。
PS:此外reader用完也要关闭,这一点也是我之前没注意到的。
关于CommandBehavior.CloseConnection的使用大家可以看看这篇文:
ExecuteReader方法中CommandBehavior.CloseConnection的一些注意事项
补充
使用 SqlDataReader
还有一个很重要的点!!就是查找到数据后要使用dataReader.Read()才能读到数据。原因如下:
摘自官方文档:
SqlDataReader 的默认位置在第一条记录前面。因此,必须调用 Read 来开始访问任何数据。
今天就被这个问题坑了好一会儿OTL
这个故事告诉我们看官方文档的重要性!!!