使用ExecuteReader时报错“阅读器关闭时尝试调用Read无效”的解决办法

问题描述


我是在写ASP.NET webform的时候遇到这个问题的。
在三层架构的DAL层有两个类,一个是FolderDal,一个是DBHelper。
想在FolderDal的GetGridViewData函数里面调用DBHelper的ExcuteReader函数,ExcuteReader函数会返回一个SqlDataReader,但是每次都会有这个错误。
后来打断点调试才发现了问题所在。

分析


下面来看看代码:

FolderDal.cs

public List 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 folderList = ConverToFolderList(reader);

            return folderList;
        }


private List ConverToFolderList(SqlDataReader reader)
        {
            List folderList = new List();

            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 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 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
这个故事告诉我们看官方文档的重要性!!!

你可能感兴趣的:(ASP.NET)