Entity Framework 4 - AddObject vs Attach

132

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

Desde mi entendimiento:

  • Use "Adjuntar" cuando ya exista una entidad en el sistema
  • Use "AddObject" al crear 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's (sin generación de código), patrón de repositorio (no trate con ctx.Persons) y Unit of Work (no trate con ctx.SaveChanges). Pero "bajo 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 Attach .

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

EDITAR

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

EDITAR 2

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

En mi ejemplo anterior para modificar una Persona existente , 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.

RPM1984
fuente
Attach también se usa en MVC hoy en día cuando se devuelven los modelos directamente a EF. Funciona bastante bien y ahorra toneladas de líneas de código.
Piotr Kula

Respuestas:

162

ObjectContext.AddObject y ObjectSet.AddObject :
Elmétodo AddObject es para agregar objetos recién creados que no existen en la base de datos. La entidad obtendrá una EntityKey temporal generada automáticamentey su EntityState se establecerá en Agregado . Cuando se llama 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, Adjuntar da como resultado un Estado deentidad sin cambios , 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 hayan 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 tienenNo se ha conectado automáticamente. Básicamente, el propósito principal de Attach es conectar entidades que ya están adjuntas al ObjectContext y que no sonnuevas, por lo que no puede usar Attach para adjuntar entidades cuyo EntityState se agrega. 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 deentidad Dirección . Supongamos que ha leído ambos objetos desde el contexto, pero no están relacionados entre sí y desea 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();
Morteza Manavi
fuente
Gracias por la respuesta, entiendo la definición de los dos (también conocidos como los dos primeros párrafos). Pero no entiendo un escenario donde NECESITO usar Attach. Su último párrafo realmente no tiene sentido para mí (se lee básicamente como una combinación de los dos primeros párrafos), ¿puede darme un ejemplo de dónde usaría "Adjuntar" en mi escenario anterior? Eso es realmente lo que estoy buscando: ejemplos, no definiciones. Aunque realmente aprecio tu tiempo. :)
RPM1984
1
No hay problema, agregué un fragmento de código para aclarar el último párrafo, ya que puede ver que tenemos 2 objetos no relacionados y Adjuntar nos ayuda a relacionarlos entre sí. El otro ejemplo sería utilizar el método Attach () para adjuntar una "entidad separada" al contexto (hay varias razones por las que es posible que desee que una entidad separada se adjunte al contexto)
Morteza Manavi,
1
Sí, te tengo ahora. Acabo de ver un video de TechEd en EF4 (por Julie Lerman), que mostró un ejemplo. Es posible que tenga una entidad que NO recuperó de una consulta (es decir, está desconectada), pero sabe que existe, por lo tanto, utiliza Adjuntar para realizar una ACTUALIZACIÓN en esa entidad. Tiene sentido, aunque todavía me cuesta imaginar un escenario en el que tenga una entidad "desconectada". Gracias por tu ayuda.
RPM1984
1
Excelente. ¿Puedo pedirle que comparta el enlace del video para otros desarrolladores que puedan leer esta publicación?
Morteza Manavi
3
El enlace anterior compartido por RPM1984 está roto, se redirige ahora a channel9.msdn.com/Events/TechEd/NorthAmerica/2010/DEV205 . Disfruta
Apilado el
31

Esta es una respuesta tardía, pero podría ayudar a otros que la encuentren.

Básicamente, una entidad "desconectada" puede suceder cuando manipula una entidad fuera del alcance "en uso".

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 alcance "en uso", la variable "e" se desconectará porque pertenece al alcance "en uso" anterior y dado que el alcance "en uso" anterior se destruye, se desconecta "e".

Así lo entiendo.

TchiYuan
fuente
3
El ejemplo de Tchi es un ejemplo excelente y simple: sí, la variable Empleado debe declararse fuera. pruebe e.Address.Street fuera del alcance y vea una ventana emergente de excepción de referencia nula. Si adjunta, la aplicación no tendrá que volver a la base de datos para el empleado en el segundo alcance.
Steve
9

Esta es una cita de Programming Entity Framework: DbContext

Llamar a Eliminar en una entidad que no es rastreada por el contexto provocará 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 solo Eliminar para marcar una entidad desconectada como Eliminada; Necesitamos 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 que una aplicación cliente obtiene un Destino existente del servidor y luego lo pasa al método DeleteDestination en el servidor. El método DeleteDestination usa el método Attach para que el contexto sepa que es un Destino existente. Luego, el método Eliminar se utiliza para registrar el Destino existente para su eliminación

Teoman shipahi
fuente
-8

¿Qué hay de referirse solo a la clave primaria 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();
Dan
fuente