快速业务通道

Trick: 巧用.NET Reflection从SqlConnection回溯到打开着的SqlDataReader

作者 佚名技术 来源 NET编程 浏览 发布时间 2012-05-20
SqlConnection SqlInternalConnection GetOpenConnection();  SqlInternalConnection SqlDataReader FindLiveReader(SqlCommand command);  SqlDataReader SqlCommand Command { get; }

由于这些方法都是internal的,我们需要依赖于.NET Reflection才能call到它们:

GetLiveDataReaderCommand

1 static string GetLiveDataReaderCommand(SqlConnection conn)
  2 {
  3     Type connType = conn.GetType();
  4     object internalConn = connType.InvokeMember("GetOpenConnection",
  5         BindingFlags.InvokeMethod | BindingFlags.Instance |  BindingFlags.NonPublic,
  6         null, conn, null);
  7     Type internalConnType = internalConn.GetType();
  8     SqlDataReader reader = internalConnType.InvokeMember ("FindLiveReader",
  9         BindingFlags.InvokeMethod | BindingFlags.Instance |  BindingFlags.NonPublic,
10         null, internalConn, new object[] { null }) as  SqlDataReader;
11     if (reader != null)
12     {
13         Type readerType = typeof(SqlDataReader);
14         SqlCommand cmd = readerType.InvokeMember("Command",
15             BindingFlags.GetProperty | BindingFlags.Instance |  BindingFlags.NonPublic,
16             null, reader, null) as SqlCommand;
17         return cmd.CommandText;
18     }
19     return null; // No live SqlDataReader
20 }

GetLiveDataReaderCommand 接收一个SqlConnection 对象,并寻找其中open着的SqlDataReader。如 果找到,则返回这个SqlDataReader对应SqlCommand的SQL语句。

当然,为了彻底根除忘关SqlDataReader的错误,推荐您还是使用using关键字从而 SqlDataReader.Dispose方法会被自动call到:

1 using (SqlDataReader reader1 = cmd.ExecuteReader())
2 {
3    while (reader1.Read())
4    {
5       // Read Fields
6    }
7 }

相信使用过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能帮助我们得到所要的信息。它们是:

凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!

分享到: 更多

Copyright ©1999-2011 厦门凌众科技有限公司 厦门优通互联科技开发有限公司 All rights reserved

地址(ADD):厦门软件园二期望海路63号701E(东南融通旁) 邮编(ZIP):361008

电话:0592-5908028 传真:0592-5908039 咨询信箱:web@lingzhong.cn 咨询OICQ:173723134

《中华人民共和国增值电信业务经营许可证》闽B2-20100024  ICP备案:闽ICP备05037997号