Entity Framework - Concorrenza

Qualsiasi sviluppatore di accesso ai dati incontra difficoltà nel rispondere alla domanda relativa alla concorrenza dei dati: "Cosa succede se più di una persona sta modificando gli stessi dati contemporaneamente?"

  • I più fortunati tra noi hanno a che fare con regole aziendali che dicono "nessun problema, vince l'ultimo in classifica".

  • In questo caso, la concorrenza non è un problema. Più probabilmente, non è così semplice e non esiste un proiettile d'argento per risolvere ogni scenario contemporaneamente.

  • Per impostazione predefinita, Entity Framework prenderà il percorso di "last one in wins", il che significa che l'ultimo aggiornamento viene applicato anche se qualcun altro ha aggiornato i dati tra l'ora in cui i dati sono stati recuperati e l'ora in cui i dati sono stati salvati.

Facciamo un esempio per capirlo meglio. L'esempio seguente aggiunge una nuova colonna VersionNo nella tabella Course.

Vai al designer e fai clic con il tasto destro sulla finestra del designer e seleziona Aggiorna modello dal database ...

Vedrai che un'altra colonna viene aggiunta in Entità corso.

Fare clic con il pulsante destro del mouse sulla colonna VersionNo appena creata e selezionare Proprietà e modificare ConcurrencyMode in Fixed come mostrato nell'immagine seguente.

Con ConcurrencyMode di Course.VersionNo impostato su Fixed, ogni volta che un corso viene aggiornato, il comando Update cercherà il corso utilizzando la sua EntityKey e la sua proprietà VersionNo.

Diamo un'occhiata a un semplice scenario. Due utenti recuperano lo stesso corso contemporaneamente e l'utente 1 cambia il titolo di quel corso in Matematica e salva le modifiche prima dell'utente 2. In seguito, quando l'utente 2 cambia il titolo di quel corso che è stato recuperato prima che l'utente 1 salvi le sue modifiche, in questo caso l'utente 2 otterrà l'eccezione di concorrenza"User2: Optimistic Concurrency exception occured".

using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;

namespace DatabaseFirstDemo {

   class Program {

      static void Main(string[] args) {

         Course c1 = null;
         Course c2 = null;

         //User 1 gets Course

         using (var context = new UniContextEntities()) {
            context.Configuration.ProxyCreationEnabled = false;
            c1 = context.Courses.Where(s ⇒ s.CourseID == 1).Single();
         }

         //User 2 also get the same Course

         using (var context = new UniContextEntities()) {
            context.Configuration.ProxyCreationEnabled = false;
            c2 = context.Courses.Where(s ⇒ s.CourseID == 1).Single();
         }

         //User 1 updates Course Title
         c1.Title = "Edited from user1";

         //User 2 updates Course Title
         c2.Title = "Edited from user2";

         //User 1 saves changes first

         using (var context = new UniContextEntities()) {

            try {
               context.Entry(c1).State = EntityState.Modified;
               context.SaveChanges();
            } catch (DbUpdateConcurrencyException ex) {
               Console.WriteLine("User1: Optimistic Concurrency exception occurred");
            }
         }

         //User 2 saves changes after User 1.
         //User 2 will get concurrency exection
         //because CreateOrModifiedDate is different in the database

         using (var context = new UniContextEntities()) {

            try {
               context.Entry(c2).State = EntityState.Modified;
               context.SaveChanges();
            } catch (DbUpdateConcurrencyException ex) {
               Console.WriteLine("User2: Optimistic Concurrency exception occurred");
            }
         }
      }
   }
}