2015-09-22 12 views
7

Utilizzando Entity Framework 6.0.0, viene visualizzata un'eccezione quando si chiude una transazione.Eccezioni durante il rollback di una transazione: connessione già chiusa?

Avevamo problemi con le modifiche simultanee alla tabella, quindi l'ho spostato in una transazione e ora ricevo eccezioni sul rollback.

Il codice:

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; 
      } 
     } 
    } 
} 

è possibile vedere la registrazione, nel codice precedente. Abbiamo anche abilitato la registrazione nel database. La traccia del registro:

=================== 
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 

I due selezioni hanno esito positivo, quindi l'inserto non funziona per qualche motivo. lanciando un'eccezione e per qualche motivo la connessione si sta chiudendo prima dell'esecuzione del rollback().

Qualche idea su cosa sto facendo male?

==== Aggiunta pila tracce ====

Stacktrace per l'eccezione esterno:

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() 

Stacktrace per l'eccezione interna:

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() 
+0

Pubblica l'eccezione completa ToString. La registrazione degli errori è insufficiente. – usr

+0

Inoltre, non si stanno commettendo le transazioni in molti casi. È davvero intenzionale? – usr

+0

Non riesco a ricreare questo su richiesta. È qualcosa che si presenta a intermittenza nel nostro ambiente di controllo qualità. Non l'ho mai visto in dev. Le uniche informazioni che ho a parte rispetto a quello che ho già postato è la traccia dello stack, che indica semplicemente che l'eccezione si è verificata nel codice che ho postato. –

risposta

6

Dopo la discussione, ha iniziato a registrare l'eccezione prima di provare a eseguire il rollback e questo ha rivelato l'errore.

L'operazione è stata un punto morto:

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. 

Da quello che sto leggendo, quando tuo detto una transazione è stata fatta una vittima deadlock, è già stato eseguito il rollback. Forse è per questo che stiamo ricevendo un'eccezione?

La correzione sembra essere quella di riconoscere quando è stato eseguito il deadlock, e non il rollback, o di non utilizzare le transazioni, e riprovare quando si verifica una violazione della chiave primaria.

+0

Non si deve chiamare Rollback mai in questa parte di codice. Inoltre, questo è un bug EF. Non dovrebbe andare in crash. Dovrebbe segnalare un errore di utilizzo dell'API o qualcosa usando un'eccezione pulita. Potresti aprire un problema su GitHub al repository EF per questo? – usr

+0

Attualmente sto cercando di non utilizzare le transazioni, ma semplicemente riprovando quando i record sottostanti sono cambiati - blocco ottimistico. –