Entity Framework 4 - AddObject vs Attach


132

Di recente ho lavorato con Entity Framework 4 e sono leggermente confuso su quando utilizzare ObjectSet.Attach e ObjectSet.AddObject .

Dalla mia comprensione:

  • Utilizzare "Allega" quando un'entità esiste già nel sistema
  • Utilizzare "AddObject" durante la creazione di una nuova entità

Quindi, se sto creando una nuova persona , lo faccio.

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

Se sto modificando una persona esistente , faccio questo:

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

Ricorda, questo è un esempio molto semplice . In realtà sto usando Pure POCO (nessuna generazione di codice), pattern di repository (non trattate con ctx.Persons) e Unit of Work (non trattate con ctx.SaveChanges). Ma "sotto le coperte", quanto sopra è ciò che accade nella mia implementazione.

Ora, la mia domanda - Devo ancora trovare uno scenario in cui ho dovuto usare Attach .

Cosa mi sto perdendo qui? Quando dobbiamo usare Attach?

MODIFICARE

Giusto per chiarire, sto cercando esempi su quando usare Attach over AddObject (o viceversa).

MODIFICA 2

La risposta di seguito è corretta (che ho accettato), ma ho pensato di aggiungere un altro esempio in cui Allegare sarebbe utile.

Nel mio esempio precedente per la modifica di una persona esistente , vengono effettivamente eseguite due query.

Uno per recuperare la persona (.SingleOrDefault) e un altro per eseguire l'AGGIORNAMENTO (.SaveChanges).

Se (per qualche ragione), sapevo già che "Joe Bloggs" esisteva nel sistema, perché fare una query aggiuntiva per farlo prima? Potrei farlo:

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

Ciò comporterà solo l'esecuzione di un'istruzione UPDATE.


L'attacco è anche usato in MVC ora un giorno in cui rimettere i modelli direttamente su EF. Funziona abbastanza bene e salva un sacco di righe di codice.
Piotr Kula,

Risposte:


162

ObjectContext.AddObject e ObjectSet.AddObject :
ilmetodo AddObject serve per aggiungere oggetti appena creati che non esistono nel database. L'entità otterrà un EntityKey temporaneo generato automaticamentee il suo EntityState verrà impostato su Aggiunto . Quando viene chiamato SaveChanges, sarà chiaro all'EF che questa entità deve essere inserita nel database.

ObjectContext.Attach e ObjectSet.Attach : d'
altra parte, Attach viene utilizzato per le entità già esistenti nel database. Invece di impostare EntityState su Aggiunto, Allega risultati in unEntityState invariato , il che significa che non è cambiato da quando è stato collegato al contesto. Si presume che gli oggetti che si stanno collegando esistano nel database. Se si modificano gli oggetti dopo che sono stati collegati, quando si chiama SaveChanges viene utilizzato il valore di EntityKey per aggiornare (o eliminare) la riga appropriata trovandone l'ID corrispondente nella tabella db.

Inoltre, usando il metodo Attach, è possibile definire relazioni tra entità già esistenti in ObjectContext ma che hannonon è stato collegato automaticamente. Fondamentalmente lo scopo principale di Attach è quello di connettere entità che sono già collegate a ObjectContext e non sononuove, quindi non è possibile utilizzare Attach per collegare entità il cui EntityState è stato aggiunto. Devi usare Aggiungi () in questo caso.

Ad esempio, supponiamo che l'entità Persona abbia una proprietà di navigazione denominata Indirizzi che è una raccolta dientità Indirizzo . Diciamo che hai letto entrambi gli oggetti dal contesto, ma non sono correlati tra loro e vuoi farlo così:

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

Grazie per la risposta, capisco la definizione dei due (ovvero i primi due paragrafi). Ma non capisco uno scenario in cui DEVO usare Attach. Il tuo ultimo paragrafo non ha davvero senso per me (legge sostanzialmente come una combinazione dei primi due paragrafi), puoi darmi un esempio di come utilizzerei "Allega" nel mio scenario sopra? È proprio quello che sto cercando: esempi, non definizioni. Apprezzo molto il tuo tempo però. :)
RPM1984,

1
Nessun problema, ho aggiunto uno snippet di codice per chiarire l'ultimo paragrafo, come puoi vedere abbiamo 2 oggetti non correlati e Attach ci aiuta a metterli in relazione tra loro. L'altro esempio potrebbe essere quello di utilizzare il metodo Attach () per ricollegare una "entità distaccata" al contesto (ci sono vari motivi per cui si potrebbe desiderare che un'entità distaccata sia ricollegata al contesto)
Morteza Manavi,

1
Sì, ho capito adesso. Ho appena visto un video TechEd su EF4 (di Julie Lerman), che ha mostrato un esempio. Potresti avere un'entità che NON hai recuperato da una query (cioè è disconnessa), ma sai che esiste, quindi usi Attach per eseguire un AGGIORNAMENTO su quell'entità. Ha senso, anche se mi sforzo ancora di immaginare uno scenario in cui si avrebbe un'entità "disconnessa". Grazie per l'aiuto.
RPM1984,

1
Grande. Posso chiederti per favore di condividere il link del video con altri colleghi sviluppatori che potrebbero leggere questo post?
Morteza Manavi,

3
Il link sopra condiviso da RPM1984 è interrotto, ora viene reindirizzato a channel9.msdn.com/Events/TechEd/NorthAmerica/2010/DEV205 . Divertiti
Accatastato il

31

Questa è una risposta tardiva, ma potrebbe aiutare gli altri a trovarla.

Fondamentalmente, un'entità "disconnessa" può accadere quando si manipola un'entità al di fuori dell'ambito "utilizzo".

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
}

Se si immette un altro ambito "using", la variabile "e" verrà disconnessa perché appartiene al precedente ambito "using" e poiché il precedente ambito "using" viene distrutto, allora "e" viene disconnesso.

Ecco come lo capisco.


3
L'esempio di Tchi è un esempio eccellente e semplice: sì, la variabile Employee dovrebbe essere dichiarata all'esterno. provare e.Address.Street al di fuori dell'ambito e visualizzare un popup di eccezione null riferimento. Se si allega, l'applicazione non dovrà tornare al DB per il Dipendente nel secondo ambito.
Steve,

9

Questa è una citazione da Programming Entity Framework: DbContext

Se si chiama Rimuovi su un'entità che non è tracciata dal contesto, verrà generata un'InvalidOperationException. Entity Framework genera questa eccezione perché non è chiaro se l'entità che si sta tentando di rimuovere sia un'entità esistente che deve essere contrassegnata per l'eliminazione o una nuova entità che deve essere semplicemente ignorata. Per questo motivo, non possiamo usare solo Rimuovi per contrassegnare un'entità disconnessa come Eliminata; dobbiamo prima collegarlo .

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

Il metodo TestDeleteDestination simula un'applicazione client recuperando una destinazione esistente dal server e quindi passandola al metodo DeleteDestination sul server. Il metodo DeleteDestination utilizza il metodo Attach per comunicare al contesto che si tratta di una destinazione esistente. Quindi il metodo Rimuovi viene utilizzato per registrare la destinazione esistente per l'eliminazione


-8

Che dire di fare riferimento solo alla chiave primaria invece di allegare?

vale a dire:

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();
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.