Trabajar con estados de entidad

En este tema se explica cómo agregar y adjuntar entidades a un contexto y cómo Entity Framework los procesa durante SaveChanges. Entity Framework se encarga del seguimiento del estado de las entidades mientras están conectados a un contexto, pero en escenarios desconectados o de N niveles, puede informar a EF en qué estado deben estar las entidades. Las técnicas que se muestran en este tema se aplican igualmente a los modelos creados con Code First y EF Designer.

Estados de entidad y SaveChanges

Una entidad puede estar en uno de los cinco estados definidos por la enumeración EntityState. Estos estados son:

  • Agregado: el contexto realiza el seguimiento de la entidad, pero aún no existe en la base de datos.
  • Sin cambios: el contexto realiza el seguimiento de la entidad y existe en la base de datos y sus valores de propiedad no han cambiado de los valores de la base de datos.
  • Modificado: el contexto realiza el seguimiento de la entidad y existe en la base de datos, y algunos o todos sus valores de propiedad se han modificado.
  • Eliminado: el contexto realiza el seguimiento de la entidad y existe en la base de datos, pero se ha marcado para su eliminación de la base de datos la próxima vez que se llame a SaveChanges.
  • Desasociado: el contexto no realiza el seguimiento de la entidad.

SaveChanges realiza diferentes acciones para las entidades en distintos estados:

  • SaveChanges no modifica las entidades sin cambios. Las actualizaciones no se envían a la base de datos para las entidades en estado Sin cambios.
  • Las entidades agregadas se insertan en la base de datos y luego se convierten en Sin cambios cuando vuelve SaveChanges.
  • Las entidades modificadas se actualizan en la base de datos y, a continuación, se convierten en Sin cambios cuando vuelve SaveChanges.
  • Las entidades eliminadas se eliminan de la base de datos y, a continuación, se desasocian del contexto.

Los siguientes ejemplos muestran formas en las que se puede cambiar el estado de una entidad o de un gráfico de entidad.

Adición de una nueva entidad al contexto

Se puede agregar una nueva entidad al contexto llamando al método Add en DbSet. Esto coloca la entidad en el estado Agregado, lo que significa que se insertará en la base de datos la próxima vez que se llame a SaveChanges. Por ejemplo:

using (var context = new BloggingContext())
{
    var blog = new Blog { Name = "ADO.NET Blog" };
    context.Blogs.Add(blog);
    context.SaveChanges();
}

Otra manera de agregar una nueva entidad al contexto es cambiar su estado a Agregado. Por ejemplo:

using (var context = new BloggingContext())
{
    var blog = new Blog { Name = "ADO.NET Blog" };
    context.Entry(blog).State = EntityState.Added;
    context.SaveChanges();
}

Por último, puede agregar una nueva entidad al contexto enlazándola con otra entidad que ya esté siendo sometida a seguimiento. Esto puede hacerse añadiendo la nueva entidad a la propiedad de navegación de colección de otra entidad o estableciendo una propiedad de navegación de referencia de otra entidad para que apunte a la nueva entidad. Por ejemplo:

using (var context = new BloggingContext())
{
    // Add a new User by setting a reference from a tracked Blog
    var blog = context.Blogs.Find(1);
    blog.Owner = new User { UserName = "johndoe1987" };

    // Add a new Post by adding to the collection of a tracked Blog
    blog.Posts.Add(new Post { Name = "How to Add Entities" });

    context.SaveChanges();
}

Tenga en cuenta que, para todos estos ejemplos, si la entidad que se va a agregar tiene referencias a otras entidades que aún no se han realizado el seguimiento, estas nuevas entidades también se agregarán al contexto y se insertarán en la base de datos la próxima vez que se llame a SaveChanges.

Asociación de una entidad existente al contexto

Si tiene una entidad que sabe que ya existe en la base de datos, pero que actualmente no está realizando el seguimiento del contexto, puede indicar al contexto que realice un seguimiento de la entidad mediante el método Attach en DbSet. La entidad estará en estado Sin cambios en el contexto. Por ejemplo:

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Blogs.Attach(existingBlog);

    // Do some more work...  

    context.SaveChanges();
}

Tenga en cuenta que no se realizará ningún cambio en la base de datos si se llama a SaveChanges sin realizar ninguna otra manipulación de la entidad adjunta. Esto se debe a que la entidad está en estado Sin cambios.

Otra manera de adjuntar una entidad existente al contexto es cambiar su estado a Sin cambios. Por ejemplo:

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Entry(existingBlog).State = EntityState.Unchanged;

    // Do some more work...  

    context.SaveChanges();
}

Tenga en cuenta que, para ambos ejemplos, si la entidad que se adjunta tiene referencias a otras entidades a las que aún no se ha realizado el seguimiento, estas nuevas entidades también se asociarán al contexto en estado Sin cambios.

Asociación de una entidad existente pero modificada al contexto

Si tiene una entidad que sabe que ya existe en la base de datos pero a la que se le pueden haber hecho cambios, puede decirle al contexto que adjunte la entidad y establezca su estado en Modificado. Por ejemplo:

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Entry(existingBlog).State = EntityState.Modified;

    // Do some more work...  

    context.SaveChanges();
}

Al cambiar el estado a Modificado, todas las propiedades de la entidad se marcarán como modificadas y todos los valores de propiedad se enviarán a la base de datos cuando se llame a SaveChanges.

Tenga en cuenta que si la entidad que se está adjuntando tiene referencias a otras entidades a las que aún no se les ha realizado un seguimiento, estas nuevas entidades se adjuntarán al contexto en el estado Sin cambios; no se convertirán automáticamente en Modificadas. Si tiene varias entidades que deben marcarse como Modificada, debe establecer el estado de cada una de estas entidades individualmente.

Cambio del estado de una entidad con seguimiento

Puede cambiar el estado de una entidad que ya se está realizando el seguimiento estableciendo la propiedad State en su entrada. Por ejemplo:

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Blogs.Attach(existingBlog);
    context.Entry(existingBlog).State = EntityState.Unchanged;

    // Do some more work...  

    context.SaveChanges();
}

Tenga en cuenta que llamar a Agregar o Adjuntar para una entidad que ya se ha realizado el seguimiento también se puede usar para cambiar el estado de la entidad. Por ejemplo, llamar a Adjuntar para una entidad que se encuentra actualmente en estado Agregado cambiará su estado a Sin cambios.

Insertar o actualizar el patrón

Un patrón común para algunas aplicaciones es Agregar una entidad como nueva (lo que da como resultado una inserción de base de datos) o Adjuntar una entidad como existente y marcarla como modificada (lo que da lugar a una actualización de base de datos) en función del valor de la clave principal. Por ejemplo, cuando se usan claves principales de enteros generadas por la base de datos, es habitual tratar una entidad con una clave cero como nueva y una entidad con una clave distinta de cero como existente. Este patrón se puede lograr estableciendo el estado de la entidad en función de una comprobación del valor de clave principal. Por ejemplo:

public void InsertOrUpdate(Blog blog)
{
    using (var context = new BloggingContext())
    {
        context.Entry(blog).State = blog.BlogId == 0 ?
                                   EntityState.Added :
                                   EntityState.Modified;

        context.SaveChanges();
    }
}

Tenga en cuenta que, al cambiar el estado a Modificado, todas las propiedades de la entidad se marcarán como modificadas y todos los valores de propiedad se enviarán a la base de datos cuando se llame a SaveChanges.