【问题标题】:"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(); 它不需要

标签: c# asp.net aspnetboilerplate transactionscope


【解决方案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 被处理了两次。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-06-13
  • 2011-12-24
  • 2011-02-01
  • 2012-09-18
  • 2015-05-05
  • 1970-01-01
相关资源
最近更新 更多