2012-10-27 7 views
8

Le chiamate al servizio Web utilizzano il codice seguente per garantire che il chiamante abbia una sessione valida. Se viene trovata una sessione valida, aggiorna i dettagli della sessione e salva le modifiche. Tutto abbastanza semplice e funziona bene.Come impedire il deadlock di EntityFramework quando si eseguono contemporaneamente queste due istruzioni

// Create the Entity Framework context 
using(MyContext ctx = CreateMyContext()) 
{ 
    // Get the user session for the client session   
    UserSession session = (from us in context.UserSessions.Include("UserEntity") 
          where us.SessionId = callerSessionId 
          select us).FirstOrDefault<UserSession>(); 

    if (session == null) 
     return false; 
    else 
    { 
     // Update session details 
     session.Calls++; 
     session.LastAccessed = DateTime.Now.Ticks; 
     Console.WriteLine("Call by User:{0}", session.UserEntity.Name); 

     // Save session changes back to the server 
     ctx.SaveChanges(); 
     return true; 
    }  
} 

Tutto funziona bene fino a quando lo stesso utente, e quindi la stessa sessione, rende più chiamate simultanee (che è perfettamente valida per accadere). In questo caso a volte ho un punto morto. Utilizzando SQL Server Profiler posso vedere quanto segue sta accadendo.

Il chiamante A esegue la selezione e acquisisce un blocco condiviso nella sessione utente. Il chiamante B esegue la selezione e acquisisce un blocco condiviso sulla stessa sessione utente. Il chiamante A non può eseguire l'aggiornamento a causa del blocco condiviso del chiamante B. Il chiamante B non può eseguire l'aggiornamento a causa del blocco condiviso del chiamante A. Deadlock.

Questo sembra uno scenario di deadlock semplice e classico e deve esistere un metodo semplice per risolverlo. Sicuramente quasi tutte le applicazioni del mondo reale hanno lo stesso problema. Ma nessuno dei libri di Entity Frameworks ha menzionato qualcosa sui deadlock.

risposta

11

Ho trovato un articolo che parla di questo HERE. In pratica sembra che tu possa avviare e interrompere una transazione che circonda la tua chiamata EF ... Il blocco fornisce il seguente esempio di codice, quindi il merito va a Diego B Vega ... Il post del blog si collega anche a un altro blog con informazioni aggiuntive.

using (var scope = new TransactionScope(TransactionScopeOption.Required, new 
    TransactionOptions { IsolationLevel= IsolationLevel.Snapshot })) 
{ 
    // do something with EF here 
    scope.Complete(); 
} 
+0

Posso fare una domanda su questo. L'uso (MyContext ctx ..) dovrebbe essere all'interno dell'uso (var scope = new TransactionScope) o viceversa? Grazie. – Raj

+0

Credo che vorrai che il 'TransactionScope' si trovi all'interno di' MyContext'. Nella mia mente solo a pensarci ... poiché la Transazione viene utilizzata * per * il Contesto ... il Contesto deve esistere al termine della Transazione. Potrebbe essere sbagliato qui però. – Jared

1
Will

il seguente lavoro per voi?

using(MyContext ctx = CreateMyContext()) 
{ 

    ctx.Database.ExecuteSqlCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;"); 


    // Get the user session for the client session   
    ... 
}