2009-06-03 7 views
4

トランザクションにラップされた2つのストアドプロシージャがあります。いろいろな理由から、データベース内ではなく、アプリケーションコード内でトランザクションを処理する必要があります。理にかなってSqlTransactionのロールバックに関する例外処理

Error message from raiserror within stored procedure. 
Transaction count after EXECUTE indicates that a COMMIT or ROLLBACK TRANSACTION statement is missing. Previous count = 1, current count = 0. 

:保存されprocsのの1にエラーが発生した場合

try 
{ 
    using (SqlConnection conn = Connection()) 
    { 
     conn.Open(); 

     using (SqlTransaction sqlTrans = conn.BeginTransaction()) 
     { 
      try 
      { 
       using (SqlCommand cmd1 = new SqlCommand("Stored_Proc_1", conn, sqlTrans)) 
       { 
        cmd1.CommandType = CommandType.StoredProcedure; 
        cmd1.ExecuteNonQuery(); 
       } 

       using (SqlCommand cmd2 = new SqlCommand("Stored_Proc_2", conn, sqlTrans)) 
       { 
        cmd2.CommandType = CommandType.StoredProcedure; 
        cmd2.ExecuteNonQuery(); 
       } 

       sqlTrans.Commit(); 
      } 
      catch 
      { 
        sqlTrans.Rollback(); 

        throw; 
      } 

     } 

     conn.Close(); 
    } 
} 

catch (SqlException ex) 
{ 
    // exception handling and logging code here... 
} 

、例外メッセージは、私のようなルックスを見ています:現時点では

、私のコードは次のようになりますなぜなら、最初のキャッチでは、トランザクションはまだロールバックされていないからです。

私は例外処理コードのためにトランザクションをロールバックしているので、私はこの件に興味がありません。 これを達成するためにコードを再構築する方法はありますか?

編集:私は私のストアドプロシージャの外のトランザクション処理を撮影したし、それはそう

create proc Stored_Proc_1 
as 

set nocount on 

begin try 
    begin transaction 

     raiserror('Error raised by Stored_Proc_1', 16, 1)  

    commit 

end try 
begin catch 
    if (@@trancount > 0) rollback 

    declare @ErrMsg nvarchar(4000), @ErrSeverity int, @ErrProc sysname, @ErrLine varchar(10) 
    select @ErrMsg = ERROR_MESSAGE(), @ErrSeverity = ERROR_SEVERITY(), @ErrProc = ERROR_PROCEDURE(), @ErrLine = ERROR_LINE() 

    -- log the error 
    -- sql logging code here... 

    raiserror(@ErrMsg, @ErrSeverity, 1) 
end catch 

がUPDATE:

が私の保存されてprocsの基本的な構造はこのようになります問題を解決した明らかに私はそれを間違っていた - でも、私はそれを正しく行う方法を知りたいです。ストアドプロシージャのトランザクションを最適なソリューションから削除していますか?

答えて

5

よく、usingによって閉鎖されます(例外の後にはClose()という奇妙なことが考えられます)。

いずれかのストアドプロシージャは、ロールバック/コミットされていないトランザクションコードを内部に作成しますか?それはのように聞こえますが、はどこに問題がありますか?

-- pseduo-TSQL 
IF @@TRANCOUNT = 0 BEGIN TRAN 
-- ... 
IF @@TRANCOUNT > 0 COMMIT TRAN -- or maybe = 1 

(条件付き行う場合:おそらく(間違った)アプローチに - どちらかといえば、エラーメッセージは、ストアドプロシージャの一つは、それがトランザクションを開始していないにもかかわらずCOMMITをやっていることを私に示唆しますトランザクションを作成したかどうかは、トランザクションを作成したかどうかを追跡する必要があります。

もう1つのオプションは、TransactionScopeを使用することです(これを設定する必要はありません)各コマンドなどに対して)少し効率が低い:

using(TransactionScope tran = new TransactionScope()) { 
    // create command, exec sp1, exec sp2 - without mentioning "tran" or 
    // anything else transaction related 

    tran.Complete(); 
} 

(ロールバックはありません。 Dispose()(via using)は必要な場合にロールバックを行います。

+0

あなたのコメントのおかげで、私は自分の格納されたprocsが間違っているとは思わない - 私は基本的な構造を含むように質問を更新しました。 – kristian

+0

待って、私はあなたが言っていることを見ていると思います.C#コードはトランザクションが存在すると予想していますが、SQLコードはそれをロールバックしていますか? – kristian

+1

まさに...行 "if(@@ trancount> 0)rollback"は特別なものです... **トランザクションが**作成されたかどうかを追跡するフラグを設定する必要があります。オープンな取引。 –

1

問題がストアドプロシージャそのものの中にある可能性があることにMarcは同意します。 いくつかの問題を概説する非常に興味深い記事がありますhere

4

アプリケーションでこれを行うと、データベース/ストアドプロシージャでトランザクションを実行しないでください。これは最も確かに混乱を招くでしょう。層を選んでそれに固執する。素敵な正規化されたデータベースを持っていることを確認してください。

BEGIN TRY 
    SET @now = CAST(@start AS datetime2(0)) 
END TRY 
BEGIN CATCH 
    SET @now = CURRENT_TIMESTAMP 
END CATCH 

、あなたは、例えば合格:ストアドプロシージャは、このようなコードが含まれている場合

0

'now'を@startとすると、tryのCASTは失敗します。これは、エラー自体が取り込まれて処理されても、トランザクションをロールバックのみとしてマークします。したがって、上記のコードから例外はありませんが、トランザクションはコミットできません。ストアドプロシージャにこのようなコードがある場合は、try/catchを避けるために書き直す必要があります。