【问题标题】:Exceptions when rolling back a transaction - connection already closed?回滚事务时出现异常 - 连接已关闭?
【发布时间】:2015-09-22 22:48:54
【问题描述】:

使用 Entity Framework 6.0.0,我在关闭事务时看到异常。

我们一直在对表进行并发更改时遇到问题,所以我将它包装在一个事务中,现在我在回滚时遇到异常。

代码:

public LockInfo getSharedLock(string jobid)
{
    using (var myDbContext = new MyDbContext())
    {
        using (var transaction = myDbContext.Database.BeginTransaction())
        {
            try
            {
                this.logger.log("Attempting to get shared lock for {0}", jobid);

                var mylocks =
                    myDbContext.joblocks.Where(j => j.customerid == this.userContext.customerid)
                        .Where(j => j.jobid == jobid)
                        .Where(j => j.operatorid == this.userContext.operatorid);

                var exclusiveLock = mylocks.FirstOrDefault(
                    j => j.lockstatus == LockInfo.LockState.Exclusive);
                if (exclusiveLock != null)
                {
                    this.logger.log("{0} already had exclusive lock, ignoring", jobid);
                    return LockInfo.populate(exclusiveLock);
                }

                var sharedLock = mylocks.FirstOrDefault(
                    j => j.lockstatus == LockInfo.LockState.Shared);
                if (sharedLock != null)
                {
                    this.logger.log("{0} already had shared lock, ignoring", jobid));
                    sharedLock.lockdt = DateTime.Now;
                    myDbContext.SaveChanges();

                    return LockInfo.populate(sharedLock);
                }

                var joblock = new joblock
                {
                    customerid = this.userContext.customerid,
                    operatorid = this.userContext.operatorid,
                    jobid = jobid,
                    lockstatus = LockInfo.LockState.Shared,
                    sharedLock.lockdt = DateTime.Now
                };

                myDbContext.joblocks.Add(joblock);
                myDbContext.SaveChanges();
                transaction.Commit();

                this.logger.log("Obtained shared lock for {0}", jobid);
                return LockInfo.populate(joblock);
            }
            catch (Exception ex)
            {
                transaction.Rollback();
                this.logger.logException(ex, "Exception in getSharedLock(\"{0}\")", jobid);
                throw;
            }
        }
    }
}

您可以在上面的代码中看到日志记录。我们也在数据库中启用了日志记录。日志跟踪:

===================
NORMAL    TicketLockController.getLock("AK2015818002WL")
===================
SQL    Opened connection at 9/22/2015 2:47:49 PM -05:00
===================
SQL    Started transaction at 9/22/2015 2:47:49 PM -05:00
===================
NORMAL    Attempting to get shared lock for AK2015818002WL
===================
SQL    SELECT TOP (1) [Extent1].[customerid] AS [customerid]
    ,[Extent1].[jobid] AS [jobid]
    ,[Extent1].[lockdtdate] AS [lockdtdate]
    ,[Extent1].[lockdttime] AS [lockdttime]
    ,[Extent1].[operatorid] AS [operatorid]
    ,[Extent1].[lockstatus] AS [lockstatus]
    ,[Extent1].[changes] AS [changes]
FROM [dbo].[joblock] AS [Extent1]
WHERE ([Extent1].[customerid] = 'TESTTK')
    AND ([Extent1].[jobid] = 'AK2015818002WL')
    AND ([Extent1].[operatorid] = 'ADMIN')
    AND (N'Exclusive' = [Extent1].[lockstatus])
===================
SQL    SELECT TOP (1) [Extent1].[customerid] AS [customerid]
    ,[Extent1].[jobid] AS [jobid]
    ,[Extent1].[lockdtdate] AS [lockdtdate]
    ,[Extent1].[lockdttime] AS [lockdttime]
    ,[Extent1].[operatorid] AS [operatorid]
    ,[Extent1].[lockstatus] AS [lockstatus]
    ,[Extent1].[changes] AS [changes]
FROM [dbo].[joblock] AS [Extent1]
WHERE ([Extent1].[customerid] = 'TESTTK')
    AND ([Extent1].[jobid] = 'AK2015818002WL')
    AND ([Extent1].[operatorid] = 'ADMIN')
    AND (N'Shared' = [Extent1].[lockstatus])
===================
SQL    INSERT [dbo].[joblock] (
    [customerid]
    ,[jobid]
    ,[lockdtdate]
    ,[lockdttime]
    ,[operatorid]
    ,[lockstatus]
    ,[changes]
    )
VALUES (
    @0
    ,@1
    ,@2
    ,@3
    ,@4
    ,@5
    ,NULL
    )
===================
SQL    Closed connection at 9/22/2015 2:47:50 PM -05:00
===================
EXCEPTION    Unhandled exception caught: The underlying provider failed on Rollback.
===================
EXCEPTION    Inner Exception: Value cannot be null.
Parameter name: connection

两次选择都成功,然后插入由于某种原因失败。抛出异常,并且由于某种原因连接在 Rollback() 执行之前关闭。

任何想法我做错了什么?

==== 添加堆栈跟踪 ====

外部异常的堆栈跟踪:

   at System.Data.Entity.Core.EntityClient.EntityTransaction.Rollback()
   at korterra.kt_api.Shared.TicketLockWrangler.getSharedLock(String jobid)
   at korterra.kt_ws.ApiControllers.Shared.TicketLockController.getSharedLock(TicketLockDTO ticketLockDTO)
   at lambda_method(Closure , Object , Object[] )
   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass10.<GetExecutor>b__9(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__18`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__0.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__18`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__2.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.Filters.AuthorizationFilterAttribute.<ExecuteAuthorizationFilterAsyncCore>d__2.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__0.MoveNext()

内部异常的堆栈跟踪:

   at System.Data.Entity.Utilities.Check.NotNull[T](T value, String parameterName)
   at System.Data.Entity.Infrastructure.Interception.DbTransactionInterceptionContext.WithConnection(DbConnection connection)
   at System.Data.Entity.Infrastructure.Interception.DbTransactionDispatcher.Rollback(DbTransaction transaction, DbInterceptionContext interceptionContext)
   at System.Data.Entity.Core.EntityClient.EntityTransaction.Rollback()

【问题讨论】:

  • 发布完整的异常 ToString。您的错误记录不足。
  • 另外,在很多情况下,您并没有提交事务。真的是故意的吗?
  • 我无法按需重新创建。这是在我们的 QA 环境中间歇性出现的东西。我在开发中从未见过它。除了我已经发布的信息之外,我拥有的唯一信息是堆栈跟踪,它只是表明我发布的代码中发生了异常。
  • 我没有提交不更改数据库的执行路径。但也许我应该。
  • 好的,我找不到错误。你迫切需要修改你的错误日志,这样它就不会丢弃这么多信息。从堆栈跟踪中,我们可以看到发生这种情况的调用和行。

标签: c# transactions entity-framework-6


【解决方案1】:

讨论后,我开始记录异常,然后尝试回滚 - 这揭示了错误。

事务已死锁:

Exception in getSharedLock("ticketnumber123456"): An error occurred while updating the entries. See the inner exception for details.

Inner Exception: An error occurred while updating the entries. See the inner exception for details.

Inner Exception: Transaction (Process ID 139) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

从我所读到的,当你告诉一个事务已经成为死锁的牺牲品时,它已经被回滚了。也许这就是我们得到异常的原因?

修复似乎是识别我们何时死锁而不回滚,或者不使用事务,并在我们遇到主键违规时重试。

【讨论】:

  • 你不应该在这段代码中调用 Rollback。此外,这是一个 EF 错误。它不应该崩溃。它应该报告 API 使用错误或使用干净异常的东西。您能否为此在 EF 存储库中打开一个 GitHub 问题?
  • 我目前正在尝试不使用事务,而只是在底层记录发生更改时重试 - 乐观锁定。
  • @usr 你能解释一下为什么不应该“在这段代码中调用回滚” - 这是什么意思?这是对回滚功能的错误使用吗?
  • 如果服务器检测到两个事务已经死锁,它会选择其中一个来杀死。当它这样做时,它将回滚事务并向客户端发送错误。作为客户端,您在这种情况下不希望回滚事务,因为事务已经回滚,再次尝试会产生另一个错误。
猜你喜欢
  • 1970-01-01
  • 2015-02-11
  • 2019-12-24
  • 2020-09-24
  • 2016-08-14
  • 2013-12-31
  • 2015-10-03
  • 2013-10-16
  • 2015-12-22
相关资源
最近更新 更多