我有一个返回OracleDataReader
对象的函数。这是:
public OracleDataReader executeCommand(string query)
{
using (conn)
{
conn.ConnectionString = connectionString;
conn.Open();
OracleCommand cmd = conn.CreateCommand();
cmd.CommandText = query;
OracleDataReader reader = cmd.ExecuteReader();
return reader;
}
}
当我试图在另一个类中使用阅读器获取数据时,我得到一个例外:
public Person GetPersonById(int id)
{
OracleDBOp db = new OracleDBOp();
String query = "select * from test_person where id=" + id;
OracleDataReader reader = db.executeCommand(query);
Person person = null;
person = new Person(Convert.ToInt32(reader["id"]),reader["first_name"].ToString(),reader["last_name"].ToString());
return person;
}
异常
读取器关闭时对GetOrdinal的尝试无效。
有什么问题?我不是在关闭读者吗?
答案 0 :(得分:4)
您的代码会在退出using
范围后立即关闭连接,因此任何读取尝试都将失败:
using (conn)
{
conn.ConnectionString = connectionString;
conn.Open();
...
return reader;
} --> Disposes of connection, which closes it, so reader can't read.
一种选择是,不是将连接放在using
块中,如果您打算将reader
传递给拥有连接的方法的控制之外,您可以指定CommandBehavior.CloseConnection
在阅读器关闭时关闭连接,但不要关闭或丢弃连接,即
OracleDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
return reader;
IMO,一种更安全的模式(假设你play nicely with the iterator,例如foreach
),而不是将资源处理给调用者,并且不将读者传递给函数,将使用yield return
,虽然这将改变您的代码的工作方式:
public IEnumerable<Person> RetrievePeople()
{
using (var conn = new OracleConnection(connString))
{
conn.Open();
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = query;
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
yield return new new Person(
Convert.ToInt32(reader["id"]),
reader["first_name"].ToString(),
reader["last_name"].ToString());
}
}
}
}
}
这样您就不需要通过阅读器了,但是在迭代器完成之前,连接将保持打开状态。这有利于保留reader
的惰性评估方法,而不会实际传递读者,并且之后不会出现who is going to clean up
连接问题。