Actualizar la fila si existe Otra Insertar lógica con Entity Framework


125

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

+2

Esto es algo que se debe hacer a nivel de motor de base de datos, en un procedimiento almacenado. De lo contrario, tendrá que ajustar la detección/actualización/inserción en una transacción. 06 abr. 112011-04-06 09:12:11

+1

@Stephen: Esto, de hecho, es lo que terminé haciendo. Gracias. 06 abr. 112011-04-06 14:18:43

  0

Jonathan, tu pregunta me es muy útil. ¿Por qué cambiaste a un procedimiento almacenado? 15 ene. 142014-01-15 09:54:35

+2

@Anar: Fue simplemente más fácil y espero mucho más eficiente. 15 ene. 142014-01-15 16:58:03

  0

¿Tiene que escribir un procedimiento almacenado para cada tabla? 03 ene. 182018-01-03 05:00:58

130

Si está trabajando con el objeto adjunto (objeto cargado de la misma instancia del contexto) puede simplemente usar:

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

// Attached object tracks modifications automatically 

context.SaveChanges(); 

si se puede usar ningún conocimiento acerca de la clave del objeto se puede 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 usted no puede decidir existencia del objeto por su ID debe exectue 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(); 
  0

Gracias. Parece lo que necesito. ¿Puedo hacerte una pregunta que me ha estado molestando por un tiempo? Normalmente, pongo mi contexto en un corto bloque 'using'. ¿Está bien dejar el contexto en la memoria por un tiempo? Por ejemplo, durante la vida de una forma de Windows?Normalmente intento limpiar los objetos de la base de datos para asegurar una carga mínima en la base de datos. ¿No hay ningún problema esperando para destruir mi contexto EF? 06 abr. 112011-04-06 02:16:58

  0

Compruebe esto: http://stackoverflow.com/questions/3653009/entity-framework-and-connection-pooling/3653392#3653392 objeto contexto debe vivir lo más breve posible, pero en el caso de winforms o wpf esto puede significar que el contexto es viviendo tanto tiempo como presentador. La pregunta vinculada contiene un enlace al artículo msdn sobre el uso de la sesión nhibernate en winforms. El mismo enfoque se puede usar para el contexto. 06 abr. 112011-04-06 05:34:14

  0

Pero, ¿qué pasa si necesito hacer esto con una lista de objetos ... en mi base de datos hay una lista de filas con el mismo ID y quiero reemplazar si existen o insertar si no ... ¿cómo lo hago? ¡Gracias! 14 oct. 112011-10-14 11:45:52

+1

Esta respuesta parece impresionante, pero me estoy encontrando con este problema en la actualización: un objeto con la misma clave ya existe en el ObjectStateManager. ObjectStateManager no puede rastrear múltiples objetos con la misma clave. 29 nov. 122012-11-29 21:58:14

+1

Parece que estaba teniendo un problema al recuperar el objeto existente para recuperar su clave antes de realizar la actualización; separar ese objeto de búsqueda primero ayudó a arreglarlo. 29 nov. 122012-11-29 22:15:28

  0

@JohnZ por lo que se separó y luego se adjunta, ¿estoy bien? ¿Es un comportamiento tan costoso en cuanto a rendimiento? Tengo el mismo problema. 15 ene. 142014-01-15 09:56:36

  0

@Anar Sí, busqué la versión de la base de datos del objeto y extraje algunos de sus valores, luego extraje esa versión, luego adjunto el objeto pasado al método, cambio su estado de objeto y lo guardo. Y sí, es bastante caro, porque haces una selección de la base de datos para cada artículo que estás actualizando (pero solo en cómo lo hago). 15 ene. 142014-01-15 15:49:03

  0

@JohnZ Tengo un problema extraño cuando busco un registro existente, cambio sus valores de campo, y cuando llamo 'SaveChanges()' de alguna manera no envía estos valores modificados a la base de datos. En cambio, envía el objeto con valores de campo sin cambios. Tengo la sensación de que ya conoces una solución a este problema. Por favor, eche un vistazo, lo apreciaría mucho. http://stackoverflow.com/questions/21088398/cannot-update-entity-framework-model 15 ene. 142014-01-15 15:56:00

  0

¿No fallaría si otros procesos externos pueden modificar/agregar el mismo registro justo después de haber recuperado dbcontext (en estado conectado) ... 24 jun. 152015-06-24 07:39:53

  0

@LadislavMrnka gracias amigo - ¿cómo cambiaría lo anterior ahora que tenemos changetracker en ef6? 14 dic. 162016-12-14 05:05:45

  0

hola gracias - otra pregunta para el último ejemplo dado: ¿reconocerá los cambios en los objetos secundarios y actualizará esos objetos? 20 dic. 162016-12-20 05:01:06

  0

@BKSpurgeon: No. También necesita detectar cambios para objetos secundarios cuando se trabaja con árbol de entidades separadas. 24 dic. 162016-12-24 08:45:44


7

Si usted sabe que usted está utilizando el mismo contexto y no separar cualquier entidad, se puede hacer una versión genérica de esta manera:

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 desde luego puede ser un campo de clase, o el método puede hacerse estática y una extensión, pero esto es lo básico.


25

A partir de Entity Framework 4.3, no es 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 

el que los doc:

añade o actualiza las entidades por clave cuando SaveChanges se llama. Equivalente a una operación de "recuperación" de la terminología de la base de datos. Este método puede ser útil cuando siembra datos usando Migraciones.


Para responder a la comment by @Smashing1978, voy a pegar partes relevantes de enlace proporcionado por @Colin

El trabajo de AddOrUpdate es asegurar que no se crea duplicados cuando se siembra datos durante el desarrollo.

En primer lugar, se ejecutará una consulta en la base de datos en busca de un registro donde lo que ha proporcionado como un (primer parámetro) clave coincide con el valor asignado columna (o valores) que se suministra en el AddOrUpdate. Por lo tanto, este es un poco flexible para emparejar, pero está perfectamente bien para sembrar datos de tiempo de diseño.

Más importante aún, si se encuentra una coincidencia, la actualización actualizará todos y anulará los que no se encuentren en su AddOrUpdate.

Dicho esto, tengo una situación en la que estoy tirando de datos desde un servicio externo e insertar o actualizar los valores existentes por clave primaria (y mis datos locales para los consumidores es de sólo lectura) - estado utilizando AddOrUpdate en la producción de más de 6 meses y hasta ahora no hay problemas.

+6

El espacio de nombres System.Data.Entity.Migrations contiene clases relacionadas con las migraciones basadas en código y sus configuraciones. ¿Hay alguna razón por la cual no deberíamos usar esto en nuestros repositorios para AddOrUpdates de la entidad no migratoria? 24 feb. 152015-02-24 20:58:55

+5

Tenga cuidado con el método AddOrUpdate: http://thedatafarm.com/data-access/take-care-with-ef-4-3-addorupdate-method/ 14 may. 152015-05-14 08:43:46


3

La respuesta de Ladislav estaba cerca, pero tuve que hacer un par de modificaciones para que esto funcione en EF6 (primero en la base de datos).Extendí mi contexto de datos con mi en AddOrUpdate método 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

La magia sucede cuando se llama a SaveChanges() y depende de la corriente EntityState. Si la entidad tiene un EntityState.Added, se agregará a la base de datos, si tiene un EntityState.Modified, se actualizará en la base de datos. Por lo que puede poner en práctica 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(); 
    } 
} 

More about EntityState

Si no se puede comprobar en Id = 0 para determinar si se trata de una entidad nueva o no, comprobar el answer of Ladislav Mrnka.


1

En mi opinión, vale la pena decir que con el recién publicado EntityGraphOperations for Entity Framework Code First puede evitar 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, code-project (incluye una demostración paso a paso y un proyecto de muestra está listo para descargar) y nuget.

Se establece automáticamente el estado de las entidades a Added o Modified. Y elegirá manualmente qué entidades se deben eliminar si ya no existe.

El ejemplo:

Digamos que tengo obtener un objeto Person. Person podría tener muchos teléfonos, un documento y podría tener 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; } 
} 

Deseo 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 clave únicas podrían desempeñar un papel al definir el estado de la entidad del teléfono. Para tales propósitos especiales, tenemos la clase ExtendedEntityTypeConfiguration<>, que hereda de EntityTypeConfiguration<>. Si queremos utilizar tales configuraciones especiales, debemos heredar nuestras clases de mapeo desde 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.


2

Insertar actualización de los demás tanto

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(); 
} 
  0

Utilicé este enfoque pero y comprobé (después del primero o predeterminado) if (query == null) 27 ene. 182018-01-27 16:40:00