2

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();
}

記憶は本当に速く成長し始めます。数秒後、例外がスローされます。通常、このコードはすべて「try /catch」セクションと「using」セクションを使用していますが、このスニペットを簡略化して、問題がコードから直接ではなくSqlClientライブラリから発生していることを示しています。

4

1 に答える 1

4

さらに調査した結果、OutOfMemoryExceptionを停止し、予想されるTimeoutExceptionを取得する方法を見つけました。

この場合、ストアドプロシージャでPRINTが使用されているため、メモリが増加しています。Drieverは、デフォルトでデータベースから出力を収集しています。したがって、ユーザーがこれを読み取っていない場合、OutOfMemoryExceptionが発生する可能性があります。

必要な結果に応じて、2つのソリューションを使用できます。

1つ目は、データベース出力が重要ではなく、実行に時間がかかる場合に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();
}

2つ目は、非常に時間がかかる可能性のある非常に時間のかかる手順を実行する場合のグーグです。タイムアウト例外は発生しません。この動作を有効にするには、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);
}
于 2013-03-20T07:14:20.090 に答える