如何在数据访问层中管理SqlDataReaders?

我试图更好地解决我的代码,代码重用等问题。

每次我想阅读一些行时,我都厌倦了输入以下内容:

using(SqlConnection conn = new SqlConnection(myConnString)) { using(SqlCommand cmd = new SqlCommand(cmdTxt, conn)) { conn.Open(); using(SqlDataReader rdr = cmd.ExecuteReader()) { while(rdr.Read()) { /* do something with rows */ } } } } 

我知道有LINQ to SQL(我不喜欢它),以及entity framework(还是个孩子)。 我没有问题必须输出我的查询,我只是不想每次都要输入命令contruction,row iterator等。

我环顾四周,发现了一些我觉得适合我的东西,并试图实现它以使我更容易。 正如您在注释中看到的,我收到SqlDataReader关闭的错误。 我猜它可能是因为DataFactory.ExecuteReader()方法中的using语句。 返回阅读器时,将在我的SqlConnection和SqlCommand变量上调用dispose方法。 我在那儿吗? 如果是这样,应该如何管理连接和命令变量?

编辑:我更新了我的代码示例,以更好地反映我正在做的事情。

 public class DataFactory { public DataFactory() {} public DataFactory(string connectionString) { _connectionString = connectionString; } protected _connectionString = "Data Source=Localhost, etc, etc"; private string ConnectionString { get{return _connectionString;} } public SqlConnection GetSqlConnection() { return new SqlConnection(ConnectionString); } public SqlDataReader ExecuteReader(string cmdTxt) { using(SqlConnection conn = new SqlConnection(ConnectionString)) { using(SqlCommand cmd = new SqlCommand(cmdTxt, conn)) { conn.Open(); return cmd.ExecuteReader(); } } } } public IRepository { T GetById(int id); } public MyTypeRepository: IRepository { private static DataFactory _df = new DataFactory(); public MyType GetById(int id) { string cmdTxt = String.Format("SELECT Name FROM MyTable WHERE ID = {0}", id); using(SqlDataReader rdr = _df.ExecuteReader(cmdTxt)) { if(rdr.Read()) /* I get an error that the reader is already closed here */ { return new MyType( Convert.ToInt32(rdr["Id"]), rdr["Name"]); } else { return null; } } } } public class MyType { public MyType(int id, string name) { _id = id; _name = name; } private string _name; public string Name { get{return _name;} } private int _id; public int Id { get{return _id;} } public override void ToString() { return string.Format("Name: {0}, Id: {1}", Name, Id); } } public class Program { private static MyTypeRepository _mtRepo = new MyTypeRepository(); static void Main() { MyType myType = _mtRepo.GetById(1); Console.WriteLine(myType.ToString()); } } 

我也想知道我正在做的事情是否有意义,或者,如果不是,如何实现类似的东西,以便我不必经常输入连接创建等。

您的方法ExecuteReader将在返回Reader之前关闭连接。 相反,它应该实现如下:

 public IDataReader ExecuteReader(string cmdTxt) { SqlConnection conn = new SqlConnection(...); try { SqlCommand cmd = new SqlCommand(cmdTxt, conn); conn.Open(); return cmd.ExecuteReader(CommandBehavior.CloseConnection); } catch { conn.Close(); throw; } } 

ExecuteReader方法的调用者需要处理IDataReader:

 using(IDataReader reader = ExecuteReader(commandText)) { ... } // reader will be disposed here and will close the connection. 

请注意,上面的内容不会在SqlCommand对象上调用Dispose。 根据我的经验以及使用Reflector查看SqlCommand,只要处理了SqlConnection,就没有必要。 但是我相信如果您想要处理它,以下内容将会起作用:

 public IDataReader ExecuteReader(string cmdTxt) { SqlConnection conn = new SqlConnection(...); SqlCommand cmd = null; try { cmd = new SqlCommand(cmdTxt, conn); conn.Open(); IDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection); cmd.Dispose(); return reader; } catch { if (cmd != null) cmd.Dispose(); conn.Close(); throw; } } 

在使用数据读取器后关闭和/或处理数据读取器非常重要,然后每个想要使用DataFactory的人都应该记住这样做。我认为返回DataTable而不是SqlDataReader是个好主意,这样你的DataFactory就不依赖了到SqlDataReader。

我的意思是 :

 public DataTable ExecuteReader(string cmdTxt) { using(SqlConnection conn = new SqlConnection(ConnectionString)) { using(SqlCommand cmd = new SqlCommand(cmdTxt, conn)) { conn.Open(); using(SqlDataReader reader=cmd.ExecuteReader()) { DataTable dt=new DataTable(); dt.Load(reader); return dt; } } } } 

编辑:好点。我也不喜欢数据表(我们使用NHibernate所以我实际上不在我们的应用程序中使用数据表)所以如果你想将数据读取器映射到你自己的对象,也许你可以有一个数据映射器将数据读取器映射到您自己的对象我的意思是:

 public T[] ExecuteReader(string cmdTxt) { using(SqlConnection conn = new SqlConnection(ConnectionString)) { using(SqlCommand cmd = new SqlCommand(cmdTxt, conn)) { conn.Open(); using(SqlDataReader reader=cmd.ExecuteReader()) { var result=new List(); while(reader.Read()) result.Add(ObjectMapper.MapReader(reader)); return result.ToArray(); } } } } 

我所做的是用查询创建一个XML文件,并使用XSLT转换生成我的DAL代码CS文件。 您可以随意使用,在XML中声明参数并在XSLT等中生成具有适当签名的方法等。我有一个博客条目,涵盖了相关主题,如何将XSLT转换集成到Visual Studio项目中 。 现在有人可能会争辩说使用类型化数据集是一回事并且是免费午餐,但在我的情况下,我使用基于BeginExecute / EndExecute的异步DAL。 没有一个VS工具能够正确使用这种方法,所以我基本上必须构建自己的方法。

我会说它并没有真正解耦 – 基本上你使用“使用System.Data.SqlClient”的任何模块都耦合到你的数据库。 DAL的重点是应用程序耦合到DAL,DAL耦合到数据库。