Oracle ODP.. NET游标泄漏
本文关键字:泄漏 游标 NET ODP Oracle | 更新日期: 2023-09-27 18:10:10
我在使用以下代码时遇到了打开游标限制问题。oracle数据库上的打开游标限制设置为1000左右。下面的代码似乎抓住了游标,即使我已经在一个using
语句(我认为),需要它的一切。(注意,我不需要从outRefCursor2中读取任何内容)
我错过了using
或其他一些清理与ODP.net?
异常在第596次迭代时始终发生。
static List<Thing> GetDetailsForItems(List<string> items) {
DateTime start = DateTime.UtcNow;
var things = new List<Thing>();
var spname = "SP_GET_THING_DETAILS";
var outRefCursorName1 = "p_ref_cursor1";
var outRefCursorName2 = "p_ref_cursor2";
// Create params
var pInput1 = new OracleParameter("p_input1",
OracleDbType.Varchar2, ParameterDirection.Input);
pInput1.Value = "";
// Input 2 can be blank
var pInput2 = new OracleParameter("p_input2",
OracleDbType.Varchar2, ParameterDirection.Input);
pInput2.Value = "";
var outRefCursor1 = new OracleParameter(outRefCursorName1,
OracleDbType.RefCursor, ParameterDirection.Output);
var outRefCursor2 = new OracleParameter(outRefCursorName2,
OracleDbType.RefCursor, ParameterDirection.Output);
int count = 0;
using (var conn = new OracleConnection(CONN_STR)) {
conn.Open();
using (var cmd = conn.CreateCommand()) {
cmd.Parameters.Add(pInput1);
cmd.Parameters.Add(pInput2);
cmd.Parameters.Add(outRefCursor1);
cmd.Parameters.Add(outRefCursor2);
cmd.CommandText = spname;
cmd.CommandType = CommandType.StoredProcedure;
foreach (string value in items) {
count++;
cmd.Parameters[pInput1.ParameterName].Value = value;
var execVal = cmd.ExecuteNonQuery();
using (var refCursor = (Types.OracleRefCursor)
cmd.Parameters[outRefCursorName1].Value) {
using (var reader = refCursor.GetDataReader()) {
while (reader.Read()) {
// read columns
things.Add(reader["COLUMN_A"].ToString());
}
} // close reader
} // close cursor
} // end foreach
} // close command
} // close connection
int seconds = (DateTime.UtcNow - start).Seconds;
Console.WriteLine("Finished in {0} seconds", seconds);
return things;
}
我使用在线找到的这个代码片段来监视DB游标。在执行代码时,我可以看到游标加起来。它们一直在cmd.ExecuteNonQuery()
线上相加。我从来没有在using语句结束后看到drop。
select sum(a.value) total_cur, avg(a.value) avg_cur, max(a.value) max_cur,
s.username, s.machine
from v$sesstat a, v$statname b, v$session s
where a.statistic# = b.statistic# and s.sid=a.sid
and b.name = 'opened cursors current'
and machine='MY COMPUTER'
group by s.username, s.machine
order by 1 desc;
即使您不使用outRefCursor2
,您仍然需要提取它并关闭它,如果它返回一个有效的游标。ODP.net不像。net版本那样很好地处理资源,因此您需要确保处理由ODP.net命令返回的所有。作为额外的步骤,在游标上显式地调用.Close()
以确保您实际上关闭了它们(尽管dispose应该负责这一点)。
您需要处理参数:
- 是的,当我们没有 时,我在显示本机资源泄漏(虚拟工作集)之前确认了这一点
我倾向于在连接的生命周期内处理参数,以防止当引用需要/想要在处理时使用连接时出现任何问题(可能是迷信)
static List GetDetailsForItems(列表项){DateTime start = DateTime. utcnow;var things = new List();var spname = "SP_GET_THING_DETAILS";var outRefCursorName1 = "p_ref_cursor1";var outRefCursorName2 = "p_ref_cursor2";
try { int count = 0; using (var conn = new OracleConnection(CONN_STR)) try { conn.Open(); // Create params var pInput1 = new OracleParameter("p_input1", OracleDbType.Varchar2, ParameterDirection.Input); pInput1.Value = ""; // Input 2 can be blank var pInput2 = new OracleParameter("p_input2", OracleDbType.Varchar2, ParameterDirection.Input); pInput2.Value = ""; var outRefCursor1 = new OracleParameter(outRefCursorName1, OracleDbType.RefCursor, ParameterDirection.Output); var outRefCursor2 = new OracleParameter(outRefCursorName2, OracleDbType.RefCursor, ParameterDirection.Output); using (var cmd = conn.CreateCommand()) { cmd.Parameters.Add(pInput1); cmd.Parameters.Add(pInput2); cmd.Parameters.Add(outRefCursor1); cmd.Parameters.Add(outRefCursor2); cmd.CommandText = spname; cmd.CommandType = CommandType.StoredProcedure; foreach (string value in items) { count++; cmd.Parameters[pInput1.ParameterName].Value = value; var execVal = cmd.ExecuteNonQuery(); using (var refCursor = (Types.OracleRefCursor) cmd.Parameters[outRefCursorName1].Value) { using (var reader = refCursor.GetDataReader()) { while (reader.Read()) { // read columns things.Add(reader["COLUMN_A"].ToString()); } } // close reader } // close cursor } // end foreach } // close command } // close connection finally { pInput1.Dispose(); pInput2.Dispose(); outRefCursorName1.Dispose(); outRefCursorName2.Dispose(); } } int seconds = (DateTime.UtcNow - start).Seconds; Console.WriteLine("Finished in {0} seconds", seconds); return things;
}
我不会选择GC.collect()…这太过分了…http://blogs.msdn.com/b/scottholden/archive/2004/12/28/339733.aspx
但是要确保处理命令对象对我有效。简单的是使用"Using"
像这样:
using(DbCommand command = dbConn1.CreateCommand())
{
command.CommandText = sql;
using (var dataReader = command.ExecuteReader())
{
dbRows = ToList(dataReader);
}
mvarLastSQLError = 0;
}
到目前为止,这些建议都没有奏效。所以在绝望中,我最后每200次迭代强制GC收集一次。使用以下代码:
if (count % 200 == 0) {
GC.Collect();
}
奇怪的是,当从单元测试中调用该方法时,手动GC.Collect()
不释放任何游标。但是当从业务层调用该方法时,它实际上确实工作,并且我可以看到通过监视oracle DB释放打开的游标。