Actualizar Fila si existe Insertar Lógica con Entity Framework


¿Alguien tiene sugerencias sobre la forma más eficiente de implementar la lógica "actualizar fila si existe, inserte" utilizando Entity Framework?

Author: Jonathan Wood, 2011-04-06

7 answers

Si está trabajando con un objeto adjunto (objeto cargado desde la misma instancia del contexto), simplemente puede usar:

if (context.ObjectStateManager.GetObjectStateEntry(myEntity).State == EntityState.Detached)
{
    context.MyEntities.AddObject(myEntity);
}

// Attached object tracks modifications automatically

context.SaveChanges();

Si puedes usar algún conocimiento sobre la clave del objeto puedes usar algo como esto:

if (myEntity.Id != 0)
{
    context.MyEntities.Attach(myEntity);
    context.ObjectStateManager.ChangeObjectState(myEntity, EntityState.Modified);
}
else
{
    context.MyEntities.AddObject(myEntity);
}

context.SaveChanges();

Si no puede decidir la existencia del objeto por su Id, debe ejecutar la consulta de búsqueda:

var id = myEntity.Id;
if (context.MyEntities.Any(e => e.Id == id))
{
    context.MyEntities.Attach(myEntity);
    context.ObjectStateManager.ChangeObjectState(myEntity, EntityState.Modified);
}
else
{
    context.MyEntities.AddObject(myEntity);
}

context.SaveChanges();
 138
Author: Ladislav Mrnka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-05 21:29:45

A partir de Entity Framework 4.3, hay un método AddOrUpdate en el espacio de nombres System.Data.Entity.Migrations:

public static void AddOrUpdate<TEntity>(
    this IDbSet<TEntity> set,
    params TEntity[] entities
)
where TEntity : class

Que por el doc :

Añade o actualiza entidades por clave cuando se llama a SaveChanges. Equivalente a una operación "upsert" de terminología de base de datos. Este método puede ser útil cuando se siembran datos mediante migraciones.


Para responder al comentario de @Smashing1978 , pegaré partes relevantes del enlace proporcionado por @Colin

El trabajo de AddOrUpdate es para asegurarse de que no cree duplicados cuando se siembran datos durante el desarrollo.

Primero, ejecutará una consulta en su base de datos buscando un registro donde lo que usted suministró como clave (primer parámetro) coincide con el valor (o valores) de columna asignados proporcionados en AddOrUpdate. Así que esto es un poco flojo-goosey para emparejar pero perfectamente bien para sembrar datos de tiempo de diseño.

Más importante aún, si se encuentra una coincidencia, la actualización se actualizará todo y anula cualquiera que no estuviera en tu AddOrUpdate.

Dicho esto, tengo una situación en la que estoy extrayendo datos de un servicio externo e insertando o actualizando valores existentes por clave primaria (y mis datos locales para los consumidores son de solo lectura)-he estado usando AddOrUpdate en producción durante más de 6 meses y hasta ahora no hay problemas.

 27
Author: Erki M.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:42

Si sabes que estás usando el mismo contexto y no separando ninguna entidad, puedes hacer una versión genérica como esta:

public void InsertOrUpdate<T>(T entity, DbContext db) where T : class
{
    if (db.Entry(entity).State == EntityState.Detached)
        db.Set<T>().Add(entity);

    // If an immediate save is needed, can be slow though
    // if iterating through many entities:
    db.SaveChanges(); 
}

db por supuesto, puede ser un campo de clase, o el método se puede hacer estático y una extensión, pero esto es lo básico.

 7
Author: ciscoheat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-12 14:21:09

La magia ocurre cuando se llama SaveChanges() y depende de la EntityState actual. Si la entidad tiene un EntityState.Added, se añadirá a la base de datos, si tiene un EntityState.Modified, se actualizará en la base de datos. Así que puedes implementar un método InsertOrUpdate() de la siguiente manera:

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

        context.SaveChanges(); 
    } 
}

Más sobre EntityState

Si no puede verificar Id = 0 para determinar si es una entidad nueva o no, verifique la respuesta de Ladislav Mrnka.

 4
Author: Stacked,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:26

La respuesta de Ladislav estuvo cerca, pero tuve que hacer un par de modificaciones para que esto funcionara en EF6 (database-first). Extendí mi contexto de datos con mi método on AddOrUpdate y hasta ahora esto parece estar funcionando bien con objetos separados:

using System.Data.Entity;

[....]

public partial class MyDBEntities {

  public void AddOrUpdate(MyDBEntities ctx, DbSet set, Object obj, long ID) {
      if (ID != 0) {
          set.Attach(obj);
          ctx.Entry(obj).State = EntityState.Modified;
      }
      else {
          set.Add(obj);
      }
  }
[....]
 3
Author: cdonner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-06 13:51:17

Insertar else actualizar ambos

public void InsertUpdateData()
{
//Here TestEntities is the class which is given from "Save entity connection setting in web.config"
TestEntities context = new TestEntities();

var query = from data in context.Employee
            orderby data.name
            select data;

foreach (Employee details in query)
{
    if (details.id == 1)
    {
        //Assign the new values to name whose id is 1
        details.name = "Sanjay";
        details. Surname="Desai";
        details.address=" Desiwadi";
    }
    else if(query==null)
     {
     details.name="Sharad";
     details.surname=" Chougale ";
     details.address=" Gargoti";
}

//Save the changes back to database.
context.SaveChanges();
}
 2
Author: Sharad Chougale,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-20 03:11:35

En mi opinión, vale la pena decir que con el recién publicado EntityGraphOperations for Entity Framework Code First puede ahorrarse escribir algunos códigos repetitivos para definir los estados de todas las entidades en el gráfico. Soy el autor de este producto. Y lo he publicado en el github, código-proyecto (incluye una demostración paso a paso y un proyecto de ejemplo está listo para descargar) y nuget.

It will set automatically the state of the entities to Added or Modified. Y elegirá manualmente qué entidades deben eliminarse si ya no existe.

El ejemplo:

Digamos que tengo obtener un objeto Person. Person podría tiene muchos teléfonos, un documento y podría tiene un cónyuge.

public class Person
{
     public int Id { get; set; }
     public string FirstName { get; set; }
     public string LastName { get; set; }
     public string MiddleName { get; set; }
     public int Age { get; set; }
     public int DocumentId {get; set;}

     public virtual ICollection<Phone> Phones { get; set; }
     public virtual Document Document { get; set; }
     public virtual PersonSpouse PersonSpouse { get; set; }
}

Quiero determinar el estado de todas las entidades que se incluyen en el gráfico.

context.InsertOrUpdateGraph(person)
       .After(entity =>
       {
            // Delete missing phones.
            entity.HasCollection(p => p.Phones)
               .DeleteMissingEntities();

            // Delete if spouse is not exist anymore.
            entity.HasNavigationalProperty(m => m.PersonSpouse)
                  .DeleteIfNull();
       });

También como usted sabe, las propiedades de clave únicas podrían desempeñar un papel mientras definir el estado de la entidad Telefónica. Para tales propósitos especiales tenemos la clase ExtendedEntityTypeConfiguration<>, que hereda de EntityTypeConfiguration<>. Si queremos usar tales configuraciones especiales, entonces debemos heredar nuestras clases de asignación de ExtendedEntityTypeConfiguration<>, en lugar de EntityTypeConfiguration<>. Por ejemplo:

public class PhoneMap: ExtendedEntityTypeConfiguration<Phone>
    {
        public PhoneMap()
        {
             // Primary Key
             this.HasKey(m => m.Id);
              …
             // Unique keys
             this.HasUniqueKey(m => new { m.Prefix, m.Digits });
        }
    }

Eso es todo.

 1
Author: Farhad Jabiyev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-20 05:44:25