在存储过程中使用TransactionScope事务不起作用

时间:2018-07-03 15:09:39

标签: sql-server entity-framework ado.net transactionscope msdtc

据我了解,将T-SQL TransactionScope包装在存储过程中时,C#BEGIN / COMMIT TRANSACTION仍然可以工作。

我有以下C#方法,该方法首先执行EF Save,然后调用具有自己事务的存储过程,然后通过HTTP调用外部服务

public async Task DoSomething(MyDto dto)
{
        using (var scope = new TransactionScope())
        {
            //Save First
            var myEntity = await _dbContext.MyEntity.Where(x=>x.Id == dto.Id).SingleOtDefaultAsync();

            // Assign properties here from dto to MyEntity and then save entity             
            await _dbContext.SaveChangesAsync();

            // call stored procedure that has its own transaction
            _dbContext.prcDoExtraWork(dto.Id);

            // call external service using Http             
            await _httpClient.PostAsync(url,somecontent)

            scope.Complete();
        }
}

存储过程:

CREATE PROCEDURE [dbo].[prcDoExtraWork]
    @ID INT 
AS
BEGIN
    SET NOCOUNT ON;
    SET ANSI_WARNINGS ON;
    SET XACT_ABORT ON;     

    BEGIN TRY
        BEGIN TRANSACTION
            // modify data and  inserts records into tables
        COMMIT TRANSACTION

        SELECT 1 AS `Result`
    END TRY
    BEGIN CATCH
        IF (XACT_STATE() <> 0)
        BEGIN       
            ROLLBACK TRANSACTION

            IF @ErrorMessage IS NULL
            BEGIN
                SET @ProcName = ERROR_PROCEDURE();
                SET @ErrorMessage = ERROR_MESSAGE();
                SET @ErrorNumber = ERROR_NUMBER();
                SET @ErrorSeverity = ERROR_SEVERITY();
                SET @ErrorState = ERROR_STATE();
            END

            EXEC prcErrorHandler @ProcName = @ProcName, 
                                 @ErrorMessage = @ErrorMessage, 
                                 @ErrorSeverity = @ErrorSeverity,
                                 @ErrorState = @ErrorState, 
                                 @ErrorNumber = @ErrorNumber

            SELECT 0 AS `Result`
        END
    END CATCH

    SET XACT_ABORT OFF;
END

问题1:通过http调用外部服务失败,我希望插入或修改的记录存储过程都将回滚。

但是这没有发生。我仍然在数据库中看到新记录

问题2

要解决上述错误,由于我使用的是异步方法,我必须启用TransactionScopeAsyncFlowOption

using (var scope = new TransactionScope(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled))
    {

     // do your stuff
     scope.complete();
   }

但是现在在scope.complete()上我得到了错误

  

由于存在以下原因,无法执行交易操作   正在处理此交易的待处理请求

     

System.Transactions.TransactionAbortedException:事务具有   流产了。 ---> System.Data.SqlClient.SqlException:事务   无法执行操作,因为有待处理的请求   正在处理此交易。在   System.Data.SqlClient.SqlConnection.OnError(SqlException异常,   布尔值breakConnection,动作为1 wrapCloseInAction),位于   System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject   stateObj,布尔值调用者HasConnectionLock,布尔值asyncClose)   System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior,   SqlCommand cmdHandler,SqlDataReader dataStream,   BulkCopySimpleResultSet bulkCopyHandler,TdsParserStateObject   stateObj,Boolean&dataReady)   System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior,   SqlCommand cmdHandler,SqlDataReader dataStream,   BulkCopySimpleResultSet bulkCopyHandler,TdsParserStateObject   stateObj)在   System.Data.SqlClient.TdsParser.TdsExecuteTransactionManagerRequest(Byte []   缓冲区,TransactionManagerRequestType请求,字符串transactionName,   TransactionManagerIsolationLevel isoLevel,Int32超时,   SqlInternalTransaction事务,TdsParserStateObject stateObj,   布尔值isDelegateControlRequest)   System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransactionYukon(TransactionRequest   transactionRequest,字符串transactionName,IsolationLevel iso,   SqlInternalTransaction internalTransaction,布尔   isDelegateControlRequest)   System.Data.SqlClient.SqlDelegatedTransaction.SinglePhaseCommit(SinglePhaseEnlistment   征集)-内部异常堆栈跟踪的结尾-在   System.Transactions.TransactionStateAborted.EndCommit(InternalTransaction   tx)位于System.Transactions.CommittableTransaction.Commit()处   System.Transactions.TransactionScope.InternalDispose()在   System.Transactions.TransactionScope.Dispose()位于   XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.d__343.MoveNext()   ---从上一个引发异常的位置开始的堆栈跟踪-   System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()在   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务   任务)在XXXXXXXXXXXXX.MyDetailController.d__9.MoveNext()   ---从上一个引发异常的位置开始的堆栈跟踪-   System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()在   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务   任务)   System.Web.Mvc.Async.TaskAsyncActionDescriptor.EndExecute(IAsyncResult   asyncResult)   System.Web.Mvc.Async.AsyncControllerActionInvoker。<> c__DisplayClass37.b__36(IAsyncResult   asyncResult)   System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult   asyncResult)   System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.b__3d()   在   System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters。<> c__DisplayClass46.b__3f()   在   System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters。<> c__DisplayClass46.b__3f()   在   System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters。<> c__DisplayClass46.b__3f()   在   System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult   asyncResult)   System.Web.Mvc.Async.AsyncControllerActionInvoker。<> c__DisplayClass21。<> c__DisplayClass2b.b__1c()   在   System.Web.Mvc.Async.AsyncControllerActionInvoker。<> c__DisplayClass21.b__1e(IAsyncResult   asyncResult)

1 个答案:

答案 0 :(得分:0)

我想我找到了。
由于我使用的是异步方法,因此我启用了TransactionScopeAsyncFlowOption解决了第一个问题

        using (var scope = new TransactionScope(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled))
        {

         // do your stuff
         scope.complete();
       }

第二期
基于成功或错误,存储的proc返回1或0。 C#代码不在乎返回结果的值,但是我没有评估存储过程的返回结果。所以要解决我必须调用'SingleOrDefault`

_dbContext.prcDoExtraWork(dto.Id).SingleOrDefault()