我使用以下代码遇到了打开游标限制问题。 oracle db上的开放游标限制设置为大约1000.以下代码似乎保留在游标上,即使我已经在using
语句(我认为)中的所有内容都需要它。 (注意,我不需要从outRefCursor2读取任何内容)
我是否错过了使用ODP.net的using
或其他一些清理工作?
异常在迭代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;
}
我正在使用在线发现的这个代码段来监控数据库游标。我可以在逐步执行代码时看到游标加起来。他们只是继续添加cmd.ExecuteNonQuery()
行。在任何使用声明关闭后,我从未看到过掉落。
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;
答案 0 :(得分:6)
即使你没有使用outRefCursor2
,你仍然需要提取它并在它返回有效游标时关闭它。 ODP.net不像.Net版本那样处理资源,所以你需要确保你处理由ODP.net命令返回的所有。作为一个额外的步骤,在游标上明确地调用.Close()
以确保你实际上关闭它们可能没什么坏处(尽管处置应该处理它)。
答案 1 :(得分:3)
您需要处理参数:
我更喜欢在连接的生命周期内处理params,以防止ref refrsrs需要/想要在dispose上使用连接时出现任何问题(可能是迷信)
静态列表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;
}
答案 2 :(得分:1)
我不会去GC.collect()......这是一种矫枉过正的行为...... http://blogs.msdn.com/b/scottholden/archive/2004/12/28/339733.aspx
但确保处理命令对象对我有用。容易使用“使用”
这样的事情:
using(DbCommand command = dbConn1.CreateCommand())
{
command.CommandText = sql;
using (var dataReader = command.ExecuteReader())
{
dbRows = ToList(dataReader);
}
mvarLastSQLError = 0;
}
答案 3 :(得分:0)
到目前为止,这些建议都没有奏效。所以在绝望中,我最终每200次迭代强制GC收集。使用以下代码。
if (count % 200 == 0) {
GC.Collect();
}
奇怪的是,当从单元测试中调用此方法时,手册GC.Collect()
不会释放任何游标。但是当从业务层调用该方法时,它确实可以正常工作,我可以看到通过监视oracle DB来释放打开的游标。