当前位置:编程学习 > C#/ASP.NET >>

SqlDataReader关闭问题

在使用SqlDataReader时候在dr.Read()结尾要加上dr.Close(),但如果在dr.Close()之前出现了异常怎么办?我看现在网上的例子都没有考虑到这种情况,是不是不需要考虑呢?比如现在的例子都是
while (dr.Read()) 
{   
 //dosomething
}
dr.Close();
如果在dosomething中出现异常呢?是否需要将dr.Close();写到finally里去? 疑问 --------------------编程问答-------------------- 使用 using, 你就不怕中间出现异常。
不明白的话,百度下using的用法。
--------------------编程问答-------------------- 写到finally里当然最保险,相当于是用了using,不过using自动调用dispose方法,而不是Close --------------------编程问答--------------------
    private static void ReadOrderData(string connectionString)
    {
        string queryString =
            "SELECT OrderID, CustomerID FROM dbo.Orders;";

        using (SqlConnection connection =
                   new SqlConnection(connectionString))
        {
            SqlCommand command =
                new SqlCommand(queryString, connection);
            connection.Open();

            SqlDataReader reader = command.ExecuteReader();

            // Call Read before accessing data. 
            while (reader.Read())
            {
                ReadSingleRow((IDataRecord)reader);
            }

            // Call Close when done reading.
            reader.Close();
        }
    }


给个msdn的示例代码,楼主学习吧
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader.aspx

--------------------编程问答--------------------
 try
 {
      while (dr.Read()) 
      {   
          //dosomething
      }
  }
 catch (Exception e)
 {
  // Handle and log error
  }
 finally 
 {
  reader.Close();
  }


再不放心就加上异常处理代码 --------------------编程问答-------------------- 有using为什么不用呢! --------------------编程问答--------------------

using (SqlConnection conn = new SqlConnection())
            {
                using (SqlCommand cmd = conn.CreateCommand())
                {
                    using (SqlDataReader reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                        { }
                    }
                }
            }
--------------------编程问答--------------------
引用 楼主 gehaochuan 的回复:
在使用SqlDataReader时候在dr.Read()结尾要加上dr.Close(),但如果在dr.Close()之前出现了异常怎么办?我看现在网上的例子都没有考虑到这种情况,是不是不需要考虑呢?比如现在的例子都是
while (dr.Read()) 
{   
 //dosomething
}
dr.Close();
如果在dosomething中出现异常……

比如说读取这样的实体

public class MyObj
{
    public string Name;
    public double Value;
}
 
的一些记录,可以在你的SQLHelper中可能有这样一个通用方法
public static IEnumerable<T> GetRecordsets<T>(string cnStr, string sql, Func<DbDataReader, T> converter)
{
    using (var conn = new SqlConnection(cnStr))
    {
        conn.Open();
        var comm = conn.CreateCommand();
        comm.CommandText = sql;
        comm.CommandType = System.Data.CommandType.Text;
        return from DbDataReader reader in comm.ExecuteReader()
                select converter(reader);
    }
}

那么,你的查询程序就可以写
var result = GetRecordsets<MyObj>(connectionString, sql, reader =>
{
    var obj = new MyObj();
    obj.Name = (string)reader["name"];
    obj.Value = (double)reader["num"];
    return obj;
});


首先你要“发现”一点,越是高级的软件它写起来越是简单。当你没有写那样的代码的“需求”时,你也反而更容易被陷在底层的争论中而左右为难。如果你不了解将来要怎样写代码,你现在可能就无法理解我后边的解释。

其次才是你的这个问题的解释。

默认情况下DbDataReader根本无需Close。

但是因为极其个别的时候,通过“曲线救国”的思路,具体地说就是通过SqlDataReader的 CommandBehavior(command行为触发器) 来关闭自己关联的 SqlConnection。说到底,目的也是为了关闭DbConnection。

因此你可以看到我所给出的代码中,你丝毫不需要调用 SqlDataReader 的 Close 方法。因为 conn 被 using(){....} 自动关闭了,这就足够了。

假设你把这个SQLHelper使用到其它的关系数据库,例如写
        public static IEnumerable<T> GetRecordsets<T>(string cnStr, string sql, Func<DbDataReader, T> converter)
        {
            using (var conn = new OracleConnection(cnStr))
            {
                conn.Open();
                var comm = conn.CreateCommand();
                comm.CommandText = sql;
                comm.CommandType = System.Data.CommandType.Text;
                return from DbDataReader reader in comm.ExecuteReader()
                       select converter(reader);
            }
        }
你会发现差别是很小的,因为它们的概念是一样的,都是保证关闭DbConnection就足够了。
不管争论“关闭还是不关闭DbDataReader”,其实并不是最终目的,都没有理解最终目的是什么。

再说using(){....}本身,你可以去搜搜看,它实现了你说的finnaly....的机制。即使using{}内部的代码抛出异常,也会调用 conn.Dispose。用不着你去编写代码,就能保证正确性。 --------------------编程问答--------------------
引用 6 楼 u010286221 的回复:
public static IEnumerable<T> GetRecordsets<T>(string cnStr, string sql, Func<DbDataReader, T> converter)
{
    using (var conn = new SqlConnection(cnStr))
    {
        conn.Open();
        var comm = conn.CreateCommand();
        comm.CommandText = sql;
        comm.CommandType = System.Data.CommandType.Text;
        return from DbDataReader reader in comm.ExecuteReader()
                select converter(reader);
    }
}

多写一个using(){....}是画蛇添足的。 --------------------编程问答-------------------- sorry,上面本来是要复制#6楼的代码:

using (SqlConnection conn = new SqlConnection())
            {
                using (SqlCommand cmd = conn.CreateCommand())
                {
                    using (SqlDataReader reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                        { }
                    }
                }
            }

这里多写一个using是画蛇添足的。 --------------------编程问答-------------------- 回7楼,在你的代码中
comm.ExecuteReader()
如何确保上一句抛出异常时,comm可以被销毁?? --------------------编程问答-------------------- 闲着没事销毁comm干什么?你知道comm.Dispose()方法内部干什么呢?不知道空谈什么销毁呢?

我们是知道为什么要调用Dispose(),采取使用using{...}的。不是因为它比较“时髦”就去调用它。

--------------------编程问答-------------------- @lz:

仅仅当你看到那个 SqlCommand 的 ExecuteReader(CommandBehavior.CloseConnection) 这种语句时,你才意识到可能需要调用这个语句返回的 SqlDataReader 的 Close()。当然,调用其 Dispose() 也就调用了其 Close()。

而假设不是这个参数,那么调用它的 Close() 或者 Dispose() 都是毫无意义的,不能因为道听途说觉得它比较“时髦”于是你就调用它。调用它除了让你的程序变慢一点还能有什么结果呢?

而假设你不想陷入这种争论中,特别是当你的SQLHelper除了给SqlConnection使用,也还给OleConnection或者Oracle或者Sqlite等等使用时,你就更无需去纠结在SqlDataReader上。那么通用地,只要保证对DbConnection保证调用其Close(),你就消解了一切关于“是否需要关闭SqlDataReader”的争论。

能够少写一些代码就要尽量少写代码,能够少浪费脑细胞去记忆一些争论就最好养成一种新的编程模式来避免争论。但是如果需要问“为什么要这样的编程模式”,我们必须知道这样编程的目的,知道深入一点的原理,决不能稀里糊涂地凭“时髦与否”来判断是否应该那样编程。 --------------------编程问答-------------------- 我们都知道,SqlConnection实际与连接池来管理的。默认地,连接池里最多只能有200个连接。这在一般来说是足够了。但是在高并发的情况下,你必须及时地调用其Close()方法,好将物理连接还给连接池,共享给别的逻辑连接。如果靠普通的GC的回收机制,那么往往并不及时,那么有可能在高并发情况下出现“连接池已满”这种异常。

仅仅对于这个数据库逻辑连接,我们需要尽快地调用它的Close()方法。或者说是通过其 IDisposable 接口的 Dispose() 方法调用其 Close()方法。对于 DbCommand、DbDabaReader等等,完全没有必要“闲着没事时”去调用其 Dispose() 方法。 --------------------编程问答-------------------- 用usin{}嘛这个好简单的嘛 --------------------编程问答-------------------- 回11楼,

闲着没事销毁comm干什么?

也就是说,SqlCommand对象实现IDisposable接口是蛋疼? --------------------编程问答-------------------- 还有,既然你说
闲着没事销毁comm干什么?
那是不是同理可以这样推理
你的这一句
using (var conn = new SqlConnection(cnStr))
    {
也应该写成
var conn = new SqlConnection(cnStr)
而不需要using对吗? --------------------编程问答-------------------- 除 --------------------编程问答-------------------- while (dr.Read()) 
{   
 //dosomething
}
dr.Close();


写到finally 是个好的习惯 --------------------编程问答--------------------
引用 13 楼 sp1234 的回复:
我们都知道,SqlConnection实际与连接池来管理的。默认地,连接池里最多只能有200个连接。这在一般来说是足够了。但是在高并发的情况下,你必须及时地调用其Close()方法,好将物理连接还给连接池,共享给别的逻辑连接。如果靠普通的GC的回收机制,那么往往并不及时,那么有可能在高并发情况下出现“连接池已满”这种异常。

仅仅对于这个数据库逻辑连接,我们需要尽快地……


非常感谢您的回答,从底层机制说明了问题,我还有个疑问,如果用using,是否意味着当前connection被从连接线程池中销毁了?如果需要频繁连接数据库是否会对性能造成影响?比如SQLHelper中都这样写的:using (SqlConnection connection = new SqlConnection(connectionString)) --------------------编程问答--------------------
引用 13 楼 sp1234 的回复:
我们都知道,SqlConnection实际与连接池来管理的。默认地,连接池里最多只能有200个连接。这在一般来说是足够了。但是在高并发的情况下,你必须及时地调用其Close()方法,好将物理连接还给连接池,共享给别的逻辑连接。如果靠普通的GC的回收机制,那么往往并不及时,那么有可能在高并发情况下出现“连接池已满”这种异常。

仅仅对于这个数据库逻辑连接,我们需要尽快地……


在再次调用  reader时候是先要关闭前一次的应该 不知道有没记错 --------------------编程问答-------------------- 对SqlConnection调用Dispose在默认情况下是放进连接池,而不是销毁,除非禁用连接池功能。除非你需要进行连续性操作,否则使用using归还到连接池完全没问题,不影响性能。而且这种情况下的归还,比自己缓存连接更稳定,会帮你消除数据库引发的事务异常。 --------------------编程问答--------------------
引用 21 楼 qldsrx 的回复:
对SqlConnection调用Dispose在默认情况下是放进连接池,而不是销毁,除非禁用连接池功能。除非你需要进行连续性操作,否则使用using归还到连接池完全没问题,不影响性能。而且这种情况下的归还,比自己缓存连接更稳定,会帮你消除数据库引发的事务异常。

再次感谢! --------------------编程问答-------------------- 有空的话可以看cyq.data 数据层框架 v4 版本的开源代码,里面封装和性能和使用方式,你都可以学习下。 --------------------编程问答-------------------- DataReader的
dispose方法里调用了colse.

 
--------------------编程问答-------------------- 除
补充:.NET技术 ,  C#
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,