Использование TransactionScope с хранимой процедурой Transaction не работает - PullRequest
0 голосов
/ 03 июля 2018

Насколько я понимаю, C # TransactionScope все еще может работать при переносе T-SQL 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 завершается неудачно, я ожидал, что любая хранимая процедура записи, вставленная или измененная, будет ROLLBACK.

Однако этого не происходит. Я до сих пор вижу новые записи в базе данных

Выпуск 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, Boolean breakConnection, Action`1 wrapCloseInAction) в System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning (TdsParserStateObject stateObj, Boolean CallerHasConnectionLock, Boolean 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 (байт [] буфер, запрос TransactionManagerRequestType, строковое имя транзакции, TransactionManagerIsolationLevel isoLevel, время ожидания Int32, Транзакция SqlInternalTransaction, TdsParserStateObject stateObj, Boolean isDelegateControlRequest) в System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransactionYukon (TransactionRequest транзакционный запрос, строковое имя транзакции, IsolationLevel iso, SqlInternalTransaction internalTransaction, Boolean isDelegateControlRequest) в System.Data.SqlClient.SqlDelegatedTransaction.SinglePhaseCommit (SinglePhaseEnlistment enlistment) --- конец трассировки стека внутренних исключений --- at 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 (Task задача) в XXXXXXXXXXXXX.MyDetailController.d__9.MoveNext () --- Конец стека трассировки из предыдущего места, где было сгенерировано исключение --- в System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () в System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (Task задача) в 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 голосов
/ 03 июля 2018

Я думаю, что нашел это.
Первая проблема, которую я решил, включив TransactionScopeAsyncFlowOption, так как я использую асинхронные методы

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

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

Второй выпуск
Сохраненный процесс возвращал 1 или 0 в зависимости от успеха или ошибки. Код C # не заботится о значении возвращаемого результата, однако я не оценивал возвращаемый результат из сохраненного процесса. Поэтому, чтобы решить, я должен вызвать 'SingleOrDefault`

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

...