“交易已中止”即使在故意中止时也会被抓住

Posted

技术标签:

【中文标题】“交易已中止”即使在故意中止时也会被抓住【英文标题】:"The transaction has aborted" getting caught even when intentionally aborting 【发布时间】:2021-07-19 14:43:09 【问题描述】:

关于我如何使用TransactionScope 的一些上下文,截至目前在程序中,如果三个数据库的查询中的任何一个出现错误,我这样做是为了让TransactionScopedispose 一切导致没有数据通过。这部分程序运行良好。

另一个背景是我使用的框架是 ASP.NET Boilerplate。

出于保密原因,以下代码将减去一些细节。

public CUDResult TriTransaction(TransactItem[] item)

    CUDResult result = new CUDResult();
    using (TransactionScope ts = new TransactionScope())
    
        try
        
            result = InsertItem(item);
            if (result.success == false)
                return result;
            ts.Complete();
        
        catch (Exception)
        
            ts.Dispose();
            return result;
        
    
    return result;

现在担心的是,我正在使用的框架会将dispose 视为服务器错误,并且始终会针对所述服务器错误An internal error occurred during your request 返回已修复的错误消息。

虽然后端按我想要的方式工作,但我希望 dispose 不会被捕获为服务器错误并导致前端显示上述错误消息。

下面将记录错误消息。

ERROR 2021-04-26 14:34:04,260 [::1] [Chrome90] [20] Abp.WebApi.ExceptionHandling.AbpApiExceptionFilterAttribute - The transaction has aborted.
System.Transactions.TransactionAbortedException: The transaction has aborted.
   at System.Transactions.TransactionStateAborted.BeginCommit(InternalTransaction tx, Boolean asyncCommit, AsyncCallback asyncCallback, Object asyncState)
   at System.Transactions.CommittableTransaction.Commit()
   at System.Transactions.TransactionScope.InternalDispose()
   at System.Transactions.TransactionScope.Dispose()
   at Abp.EntityFramework.Uow.TransactionScopeEfTransactionStrategy.Commit()
   at Abp.EntityFramework.Uow.EfUnitOfWork.CompleteUow()
   at Abp.Domain.Uow.UnitOfWorkBase.Complete()
   at Abp.Domain.Uow.UnitOfWorkInterceptor.PerformSyncUow(IInvocation invocation, UnitOfWorkOptions options)
   at Abp.Domain.Uow.UnitOfWorkInterceptor.PerformUow(IInvocation invocation, UnitOfWorkOptions options)
   at Abp.Domain.Uow.UnitOfWorkInterceptor.Intercept(IInvocation invocation)
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Abp.Auditing.AuditingInterceptor.PerformSyncAuditing(IInvocation invocation, AuditInfo auditInfo)
   at Abp.Auditing.AuditingInterceptor.Intercept(IInvocation invocation)
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Abp.Runtime.Validation.Interception.ValidationInterceptor.Intercept(IInvocation invocation)
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Castle.Proxies.XXXAppServiceProxy.TriTransaction(TransactItem[] item)
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Abp.WebApi.Controllers.Dynamic.Interceptors.AbpDynamicApiControllerInterceptor`1.Intercept(IInvocation invocation)
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Castle.Proxies.DynamicApiController`1Proxy_4.TriTransaction(TransactItem[] item)
   at lambda_method(Closure , Object , Object[] )
   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass6_2.<GetExecutor>b__2(Object instance, Object[] methodParameters)
   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)
   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken)
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Tracing.ITraceWriterExtensions.<TraceBeginEndAsyncCore>d__17`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Tracing.ITraceWriterExtensions.<TraceBeginEndAsyncCore>d__17`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Abp.WebApi.Uow.AbpApiUowFilter.<ExecuteActionFilterAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Tracing.ITraceWriterExtensions.<TraceBeginEndAsyncCore>d__17`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Abp.WebApi.Validation.AbpApiValidationFilter.<ExecuteActionFilterAsync>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Tracing.ITraceWriterExtensions.<TraceBeginEndAsyncCore>d__17`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Abp.WebApi.Auditing.AbpApiAuditFilter.<ExecuteActionFilterAsync>d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Tracing.ITraceWriterExtensions.<TraceBeginEndAsyncCore>d__17`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Abp.WebApi.Security.AntiForgery.AbpAntiForgeryApiFilter.<ExecuteAuthorizationFilterAsync>d__10.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Tracing.ITraceWriterExtensions.<TraceBeginEndAsyncCore>d__17`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Abp.WebApi.Authorization.AbpApiAuthorizeFilter.<ExecuteAuthorizationFilterAsync>d__7.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Tracing.ITraceWriterExtensions.<TraceBeginEndAsyncCore>d__17`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Controllers.AuthenticationFilterResult.<ExecuteAsync>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Controllers.ExceptionFilterResult.<ExecuteAsync>d__6.MoveNext()

我怀疑这可能不仅仅是 dispose 被作为错误捕获的事实,而且可能完全是另外一回事,但我对此没有足够的知识,想知道是否有任何方法可以解决每当有意处置事务时前端总是显示An internal error occurred during your request 错误消息的问题。

【问题讨论】:

只有在InsertItem(item) 抛出错误时才会出现这种情况吗? 在 InsertItem(item) 中捕获错误的任何时候,result.success 将设置为 false 并返回结果,根据我对 TransactionScope 工作原理的理解有效调用 ts.dispose .所以在某种程度上,是的,它只发生在 InsertItem(item) 抛出错误时。 我需要重新表述我的问题:只有当 InsertItem(item) 内抛出的异常冒泡到 using 块内的 catch (Exception) 时才会发生这种情况? 那么你的问题可能是你调用了两次 dispose 。 using 语句实际上只是 TransactionScope ts = new TransactionScope; try ... finally ts.Dispose() 的语法糖。查看this 看看它是如何在代码中实现的 您正在处理两次,因为您有一个 using 块。只需删除行 ts.Dispose(); 它不需要 【参考方案1】:

您的问题是您两次调用Dispose,您永远不需要手动调用Dispose,这就是using 语句的用途。 using (...) 基本上只是一些 syntactic sugar 用于 try/finally 块。所以这个:

using (var foo = new Foo())

    foo.SayHello();

编译为

Foo foo = new Foo();
try

    foo.SayHello();

finally

    if (foo != null)
    
        ((IDisposable)foo).Dispose();
    

意味着你的代码被编译为

CUDResult result = new CUDResult();
TransactionScope ts = new TransactionScope();
try

    try
    
        result = InsertItem(item);
        if (!result.success) // instead of result.success == false
            return result;
        ts.Complete();
    
    catch (Exception)
    
        ts.Dispose();
        return result;
    

finally

    if (ts != null)
    
        ((IDisposable)ts).Dispose();
    


return result;

这很明显,当捕获到异常时,您将处理 ts 两次

【讨论】:

我已经删除了dispose,但错误仍然出现在前端。我感谢您的解释,但我不认为导致前端错误的问题是因为 ts 被处理了两次。

以上是关于“交易已中止”即使在故意中止时也会被抓住的主要内容,如果未能解决你的问题,请参考以下文章

c ++如何创建一个即使在对象被覆盖时也会持续的指针?

即使目录可写,全局安装时也会出现 NPM 错误

即使 IAM 角色具有完整的 Redshift 权限,AWS Lambda 在调用 Redshift 的“CreateCluster”操作时也会出现“拒绝访问”错误

为什么dev_appserver.py即使在空闲时也会使用这么多CPU?

Spring Boot 2.0.3 Oauth2 安全性:即使在标头中使用访问令牌时也会出现 401 错误

即使在 res.send 之后,代码也会被执行