2017-05-01 6 views
0

SQL Server 2014でストアドプロシージャを実行するC#で書かれた自動スクリプトがあります。ストアドプロシージャは複数のselectステートメント、updateステートメント、insertステートメントを実行しています。例外が発生したときにトランザクション全体をキャッチしてロールバックします。C#キャッチにSql例外が渡されない

それは次のようになります。プロシージャを呼び出す

BEGIN TRY 
    BEGIN TRANSACTION TransName 
    --Lots of SQL! 
    COMMIT TRANSACTION TransName 
END TRY 
BEGIN CATCH 
    ROLLBACK TRANSACTION TransName; 
    THROW 
END CATCH  

私のC#は、このようになります。

using (SqlCommand Command = new SqlCommand(query, Connection)) 
{ 
    // Retry several times if the query fails. 
    for (var retry = 0; retry < 5 && !Success; ++retry) 
    { 
     try 
     { 
      Command.ExecuteNonQuery(); 
      Success = true; 
     } 
     catch (SqlException e) 
     { 
      // Handling for Timeout or deadlocks. 
      // If not a timeout or deadlock and retry hasn't happened 4 times already. 
      if (!(e.Number == 1205 || e.Number == 1204 || e.Number == -2) || retry == 4) 
      { 
       LogException(e); 
      } 
      else if (e.Number == 1205 || e.Number == 1204) 
      { 
       // Wait to avoid hammering the database. 
       Thread.Sleep(500); 
      } 
      else if (e.Number == -2) 
      { 
       // Wait to avoid hammering the database. 
       Thread.Sleep(5000); 
      } 

      Success = false; 
     } 
    } 
} 

私はそれが存在する場合、SQLが通過することを確認するためにループしています自動化されたスクリプトなので、デッドロックやタイムアウトが発生します。

はそれが可能です:私は、ストアドプロシージャが例外をログに記録しませんでしたが、データのどれも私の質問に私をもたらし手続きタッチテーブルに存在しないことを確認することができますスクリプトの私のログに

例外がT-SQLで捕捉された後にT-SQL THROWステートメントを使用して再びスローされますが、例外はC#クライアントにスローされません。

何かを明確にすることができるかどうか教えてください。ありがとう!

答えて

0

ファーストをオフに存在する場合、あなたはあなたの提案のための@MadMyche感謝します何らかの理由でヒットしてしまった。

何が起きているのかは、クエリが実行されているときにタイムアウトし、C#コードのリトライループに返されることがありました。このとき、クエリで開かれたトランザクションは閉じられませんでした。リトライループが最後にループバックしてクエリを正常に終了すると、sql接続が閉じられ、SQLエンジンが終了して閉じられ、開いているトランザクションはロールバックされ、保存されたデータは削除されます。

私は何が起こるかを説明し、この記事を見つけたとソリューション提供:私は何

http://www.sommarskog.se/error_handling/Part1.html

http://weblogs.sqlteam.com/dang/archive/2007/10/20/Use-Caution-with-Explicit-Transactions-in-Stored-Procedures.aspx

をそれから私は、その再施行ソリューション別の記事を見つけましたそれを解決するには、プロキシの先頭にXACT_ABORTを設定していました。

XACT_ABORTの設定

はXACT_ABORTは、接続する前にロールバックされますトランザクションONに設定すると

documentationを参照してください)「のTransact-SQLステートメントは、実行時エラーが発生すると、SQL Serverは自動的に現在のトランザクションをロールバックするかどうかを指定します」ロールバックされる予定のオープントランザクションがないように、C#クライアントによって閉じられます。

0

他の例外タイプをキャッチしてログに記録しますか? SqlException以外の何かがスローされるとどうなりますか?それは記録されていますか?

リトライロジックについて - 私はInvalidOperationExceptionも処理します。 Connectionが開いていない場合、ExecuteNonQueryはInvalidOperationExceptionをスローします。たとえば、接続が簡単なネットワーク停止などによりConnectionState.Broken状態になることがあります。デッドロックやタイムアウトを再試行する方法と同様に、InvalidOperationExceptionをキャッチし、接続状態をチェックし、開いていない場合は再オープンして再試行します。

+0

上記のコードは、SQLクエリの実行を容易にする方法に含まれています。このメソッドが呼び出されると、スローされた可能性がある他のものを記録する 'try {} catch(Exception ex){}'にラップされるので、C#コードで捕捉される例外はありません。私はInvalidOperationExceptionを調べます。ありがとう! – Shmoman

+0

この場合、何も記録されない理由はわかりません。キャッチブロック内のスローで例外が再スローされます(少なくとも、私はそれがない状況を認識していません:))。 InvalidOperationExceptionを処理すると、潜在的にコードをより弾力的にする必要がありますが、ログに何も含まれていない理由は説明されません。おそらくあなたが提供したコードスニペットの外にあるものが問題の原因になっています。 – shurik

1

SQLでtry...catchは少し異なる動作しますが、私が過去に行ったことは、ストアドプロシージャのOUTPUT変数を使用することです:

ALTER PROCEDURE dbo.yourStoredProcedure 
    (-- your parameters 
    @errNumber INT OUTPUT, 
    @errLine INT OUTPUT, 
    @errMessage VARCHAR(MAX) OUTPUT) 
AS 
BEGIN 

    SET @errNumber = 0 
    SET @errLine = 0 
    SET @errMessage = '' 

    BEGIN TRY 
     BEGIN TRANSACTION TransName 
     --Lots of SQL! 
     COMMIT TRANSACTION TransName 
    END TRY 
    BEGIN CATCH 
     ROLLBACK TRANSACTION TransName; 

     SELECT @errNumber = ERROR_NUMBER() 
     ,  @errLine = ERROR_LINE() 
     ,  @errMessage = ERROR_MESSAGE() 
    END CATCH 
END 
GO 

をそして、あなたは追加するにはC#の内tryを調整する必要がありますパラメータと戻りが

try 
    { 
     SqlParameter errNumber = new SqlParameter("@errNumber", 0); 
     SqlParameter errLine = new SqlParameter("@errLine", 0); 
     SqlParameter errMessage = new SqlParameter("@errMessage", ""); 

     Command.ExecuteNonQuery(); 

     int SqlError = (int)(errNumber.Value); 
     int SqlLine = (int)(errNumber.Value); 
     string SqlMessage = (string)errMessage.Value; 

     if (SqlError == 0) { Success = true; } 
     else { 
      Success = false; 
      // whatever else you want to do with the error data 
     } 
    } 

あなたSqlExceptionキャッチはまだ手続きTRY...CATCH内なかったエラーをキャッチだろう、とあなたもGENERを持つべき値の読み取りic Catch(Exception ex)ブロックで他のエラーが発生していて、最後に必要なクリーンアップについてはfinally {}を忘れないようにしてください。 try...catch内のトランザクションを包む

更新ほとんどの場合には2017年5月3日

は、uncommitable取引につながります。したがって、トランザクション内でtry-catchを持つようにラップを反転することができます。エラーが検出された場合、エラー値を取得できるはずです。また、トランザクションが存在する場合は、(@@transcount >0)がロールバックされ、@@ transcountが0に減少します。try-catchブロックが閉じられた後、私たちは再び私はキャッチがなかったことを知って助けた出力パラメータを追加し、@@ transountをチェックし、コミット1は

BEGIN TRANSACTION TransName 

BEGIN TRY 
    --Lots of SQL! 
END TRY 

BEGIN CATCH 
    SELECT @errNumber = ERROR_NUMBER() 
    ,  @errLine = ERROR_LINE() 
    ,  @errMessage = ERROR_MESSAGE() 

    IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION TransName 
END CATCH 

IF (@@TRANCOUNT > 0) COMMIT TRANSACTION TransName 
+0

私はこれをコードの後半で扱う一般的な 'Catch(Exception ex)'を持っています。出力変数を試して何かが現れるかどうかを確認します。 – Shmoman

+0

出力変数を追加しましたが、何も出力されません。エラーはtry catchではないことがわかります変更をロールバックします。 – Shmoman

+0

プロシージャを更新しました –

関連する問題