我有执行Sql的问题,实际上是在SqlServer上简单调用存储过程。
考虑以下Sql存储过程:
CREATE PROCEDURE InfiniteLoop
AS
BEGIN
DECLARE @ixi NUMERIC(38) = 0
DECLARE @i NUMERIC(38) = 0
WHILE 1 = 1
BEGIN
SET @i = @i + 1
SET @ixi = @ixi * @i
PRINT N'some text'
END
END;
现在我以这种方式从C#调用此过程:
public void CallProcedure()
{
SqlCommand cmd = this._connection.CreateCommand();
cmd.CommandType = CommandType.StoredProcedure;
command.CommandText = 'InfiniteLoop';
//Line below throws OutOfMemoryException
cmd.ExecuteNonQuery();
cmd.Dispose();
}
记忆开始快速增长。几秒钟后抛出异常。 Normaly所有这些代码都使用' try / catch'并且'使用'部分,但我简化了这个片段,以显示问题来自SqlClient库而不是直接来自我的代码。
答案 0 :(得分:4)
经过更多的研究,我找到了解决方法,如何停止OutOfMemoryException并获取预期的TimeoutException。
我这种情况因为存储过程中使用的PRINT而增长。 Driever默认收集数据库的输出。因此,如果用户没有读取此信息,则可能发生OutOfMemoryException。
根据您想要的结果,可以使用两种解决方案。
当数据库输出对您不重要时,第一个是好的,并且当执行需要很长时间时您期望Timout。下面的代码片段以这种方式解决了问题:
public void CallProcedure()
{
// Line below release all Errors/PRINT output from command. Command is now
// not collecting them, so memory is not growing.
// CommandTimeout will be thrown after preset value on command object.
this._connection.FireInfoMessageEventOnUserErrors = true;
SqlCommand cmd = this._connection.CreateCommand();
cmd.CommandTimeout = 15;
cmd.CommandType = CommandType.StoredProcedure;
command.CommandText = 'InfiniteLoop';
//Line below throws OutOfMemoryException
cmd.ExecuteNonQuery();
cmd.Dispose();
}
当你想要执行花费大量时间的耗时程序时,第二个是goog。永远不会发生超时异常。要启用此行为,您需要在SqlConnection中的InfoMessage上附加SqlInfoMessageEventHandler。请参阅以下代码段:
public void CallProcedure()
{
// By attaching this event no Timeout exception on ExecuteNonQuery occur
// Your ExecuteNonQuery can hang infinitly!
// If you need Timeout then you need to write you own handler from different thread
this._connection.InfoMessage += new SqlInfoMessageEventHandler(OnInfoMessage);
// Line below release all Errors/PRINT output from command. Command is now
// not collecting them so memory is not growing.
this._connection.FireInfoMessageEventOnUserErrors = true;
SqlCommand cmd = this._connection.CreateCommand();
// In this case Timeout will never occur
cmd.CommandTimeout = 15;
cmd.CommandType = CommandType.StoredProcedure;
command.CommandText = 'InfiniteLoop';
//Line below throws OutOfMemoryException
cmd.ExecuteNonQuery();
cmd.Dispose();
this._connection.InfoMessage -= new SqlInfoMessageEventHandler(OnInfoMessage);
}
void OnInfoMessage(object sender, SqlInfoMessageEventArgs e)
{
System.Diagnostics.Debug.WriteLine(DateTime.Now.ToString()+": "+ e.Message);
}