Entity Framework - Concurrencia

Cualquier desarrollador de acceso a datos se enfrenta a dificultades al responder la pregunta sobre la concurrencia de datos: "¿Qué sucede si más de una persona está editando los mismos datos al mismo tiempo?"

  • Los más afortunados entre nosotros se enfrentan a reglas comerciales que dicen "no hay problema, el último en gana".

  • En este caso, la concurrencia no es un problema. Lo más probable es que no sea tan simple como eso, y no existe una fórmula mágica para resolver todos los escenarios a la vez.

  • De forma predeterminada, Entity Framework tomará la ruta de "último en victorias", lo que significa que la última actualización se aplica incluso si alguien más actualizó los datos entre el momento en que se recuperaron y el momento en que se guardaron los datos.

Tomemos un ejemplo para entenderlo mejor. El siguiente ejemplo agrega una nueva columna VersionNo en la tabla del curso.

Vaya al diseñador y haga clic derecho en la ventana del diseñador y seleccione actualizar el modelo de la base de datos ...

Verá que se agrega otra columna en la Entidad del curso.

Haga clic con el botón derecho en la columna recién creada VersionNo y seleccione Propiedades y cambie ConcurrencyMode a Fixed como se muestra en la siguiente imagen.

Con ConcurrencyMode de Course.VersionNo establecido en Fixed, cada vez que se actualiza un curso, el comando Actualizar buscará el curso utilizando su EntityKey y su propiedad VersionNo.

Echemos un vistazo a un escenario sencillo. Dos usuarios recuperan el mismo curso al mismo tiempo y el usuario 1 cambia el título de ese curso a Matemáticas y guarda los cambios antes que el usuario 2. Más tarde, cuando el usuario 2 cambia el título de ese curso que se recuperó antes de que el usuario 1 guardara sus cambios, en ese caso de que el usuario 2 obtenga una excepción de concurrencia"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");
            }
         }
      }
   }
}