SqlCommand ExecuteNonQuery抛出OutOfMemoryException

时间:2013-03-19 13:07:30

标签: out-of-memory sqlcommand executenonquery

我有执行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库而不是直接来自我的代码。

1 个答案:

答案 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);
}