据我了解,将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)
答案 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()