Entity Framework 4 – AddObject vs Attach

Recientemente he estado trabajando con Entity Framework 4 y estoy un poco confundido sobre cuándo usar ObjectSet.Attach y ObjectSet.AddObject .

Desde mi entendimiento:

  • Use “Adjuntar” cuando una entidad ya existe en el sistema
  • Use “AddObject” cuando cree una nueva entidad

Entonces, si estoy creando una nueva Persona , hago esto.

var ctx = new MyEntities(); var newPerson = new Person { Name = "Joe Bloggs" }; ctx.Persons.AddObject(newPerson); ctx.SaveChanges(); 

Si estoy modificando una Persona existente , hago esto:

 var ctx = new MyEntities(); var existingPerson = ctx.Persons.SingleOrDefault(p => p.Name = "Joe Bloggs" }; existingPerson.Name = "Joe Briggs"; ctx.SaveChanges(); 

Tenga en cuenta que este es un ejemplo muy simple . En realidad estoy usando Pure POCO (sin generación de código), el patrón Repository (no se ocupan de ctx.Persons), y la Unidad de trabajo (no se ocupan de ctx.SaveChanges). Pero “debajo de las sábanas”, lo anterior es lo que sucede en mi implementación.

Ahora, mi pregunta : todavía no he encontrado un escenario en el que haya tenido que usar Adjuntar .

que me estoy perdiendo aqui? ¿Cuándo necesitamos usar Adjuntar?

EDITAR

Solo para aclarar, estoy buscando ejemplos de cuándo usar Adjuntar sobre AddObject (o viceversa).

EDIT 2

La respuesta a continuación es correcta (lo cual acepté), pero pensé que agregaría otro ejemplo donde Attach sería útil.

En mi ejemplo anterior para modificar una Persona existente , actualmente se están ejecutando dos consultas.

Uno para recuperar la Persona (.SingleOrDefault), y otro para realizar la ACTUALIZACIÓN (.SaveChanges).

Si (por alguna razón), ya sabía que “Joe Bloggs” existía en el sistema, ¿por qué hacer una consulta adicional para obtenerlo primero? Yo podría hacer esto:

 var ctx = new MyEntities(); var existingPerson = new Person { Name = "Joe Bloggs" }; ctx.Persons.Attach(existingPerson); ctx.SaveChanges(); 

Esto dará como resultado que solo se ejecute una instrucción UPDATE.

ObjectContext.AddObject y ObjectSet.AddObject :
El método AddObject es para agregar objetos creados recientemente que no existen en la base de datos. La entidad obtendrá una EntityKey temporal generada automáticamente y su EntityState se establecerá en Added . Cuando se llama a SaveChanges, el EF tendrá claro que esta entidad debe insertarse en la base de datos.

ObjectContext.Attach y ObjectSet.Attach :
Por otro lado, Attach se usa para entidades que ya existen en la base de datos. En lugar de establecer EntityState en Agregado, Adjunte resultados en un EntityState sin modificar , lo que significa que no ha cambiado desde que se adjuntó al contexto. Se supone que los objetos que está adjuntando existen en la base de datos. Si modifica los objetos después de que se han adjuntado, cuando llama a SaveChanges, el valor de EntityKey se usa para actualizar (o eliminar) la fila adecuada al encontrar su ID coincidente en la tabla db.

Además, utilizando el método Attach, puede definir relaciones entre entidades que ya existen en ObjectContext pero que no se han conectado automáticamente. Básicamente, el objective principal de Attach es conectar entidades que ya están asociadas al ObjectContext y no son nuevas, por lo que no puede usar Attach para adjuntar entidades cuyo EntityState se haya agregado. Tienes que usar Add () en este caso.

Por ejemplo, supongamos que su entidad Persona tiene una propiedad de navegación llamada Direcciones que es una colección de entidad de Dirección . Digamos que ha leído ambos Objetos desde el contexto, pero no están relacionados entre sí y quiere que sea así:

 var existingPerson = ctx.Persons.SingleOrDefault(p => p.Name = "Joe Bloggs" }; var myAddress = ctx.Addresses.First(a => a.PersonID != existingPerson.PersonID); existingPerson.Addresses.Attach(myAddress); // OR: myAddress.PersonReference.Attach(existingPerson) ctx.SaveChanges(); 

Esta es una respuesta tardía, pero podría ayudar a otros a encontrar esto.

Básicamente, una entidad “desconectada” puede suceder cuando manipulas una entidad fuera del scope “usar”.

 Employee e = null; using (var ctx = new MyModelContainer()) { e = ctx.Employees.SingleOrDefault(emp => emp .....); } using (var ctx2 = new MyModelContainer()) { e; // This entity instance is disconnected from ctx2 } 

Si ingresa otro ámbito de “uso”, la variable “e” se desconectará porque pertenece al ámbito de “uso” anterior y, dado que se destruye el ámbito de “uso” anterior, se desconectará “e”.

Así es como lo entiendo.

Esta es una cita de Programming Entity Framework: DbContext

Llamar a Quitar en una entidad que no esté rastreada por el contexto hará que se genere una InvalidOperationException. Entity Framework arroja esta excepción porque no está claro si la entidad que está tratando de eliminar es una entidad existente que debe marcarse para su eliminación o una nueva entidad que simplemente debe ignorarse. Por esta razón, no podemos usar simplemente Eliminar para marcar una entidad desconectada como Eliminada; tenemos que adjuntarlo primero .

 private static void TestDeleteDestination() { Destination canyon; using (var context = new BreakAwayContext()) { canyon = (from d in context.Destinations where d.Name == "Grand Canyon" select d).Single(); } DeleteDestination(canyon); } private static void DeleteDestination(Destination destination) { using (var context = new BreakAwayContext()) { context.Destinations.Attach(destination); context.Destinations.Remove(destination); context.SaveChanges(); } } 

El método TestDeleteDestination simula una aplicación cliente que recupera un destino existente del servidor y luego lo pasa al método DeleteDestination en el servidor. El método DeleteDestination utiliza el método Attach para que el contexto sepa que se trata de un Destino existente. Luego, el método Remove se usa para registrar el Destino existente para su eliminación

¿Qué hay de solo referirse a la clave principal en lugar de adjuntar?

es decir:

 var existingPerson = ctx.Persons.SingleOrDefault(p => p.Name = "Joe Bloggs" }; var myAddress = ctx.Addresses.First(a => a.PersonID != existingPerson.PersonID); existingPerson.AddressId = myAddress.Id // not -> existingPerson.Addresses.Attach(myAddress); // OR: myAddress.Person.Id = existingPerson.Id // not -> myAddress.PersonReference.Attach(existingPerson); ctx.SaveChanges();