Entity Framework 4 - AddObject vs Attach


132

Ich habe kürzlich mit Entity Framework 4 gearbeitet und bin etwas verwirrt darüber, wann ObjectSet.Attach und ObjectSet.AddObject verwendet werden sollen .

Meinem Verständnis nach:

  • Verwenden Sie "Anhängen", wenn bereits eine Entität im System vorhanden ist
  • Verwenden Sie "AddObject", wenn Sie eine brandneue Entität erstellen

Wenn ich also eine neue Person erstelle , mache ich das.

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

Wenn ich eine vorhandene Person ändere , mache ich Folgendes :

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

Denken Sie daran, dies ist ein sehr einfaches Beispiel. In Wirklichkeit verwende ich Pure POCOs (keine Codegenerierung), Repository-Muster (nicht mit ctx.Persons) und Unit of Work (nicht mit ctx.SaveChanges). Aber "under the cover" ist das Obige, was in meiner Implementierung passiert.

Nun meine Frage - ich muss noch ein Szenario finden, in dem ich Attach verwenden musste .

Was fehlt mir hier? Wann müssen wir Attach verwenden?

BEARBEITEN

Zur Verdeutlichung suche ich nach Beispielen, wann Attach over AddObject verwendet werden soll (oder umgekehrt).

BEARBEITEN 2

Die folgende Antwort ist richtig (was ich akzeptiert habe), aber ich dachte, ich würde ein weiteres Beispiel hinzufügen, bei dem Anhängen nützlich wäre.

In meinem obigen Beispiel zum Ändern einer vorhandenen Person werden tatsächlich zwei Abfragen ausgeführt.

Eine zum Abrufen der Person (.SingleOrDefault) und eine zum Ausführen des UPDATE (.SaveChanges).

Wenn ich (aus irgendeinem Grund) bereits wusste, dass "Joe Bloggs" im System vorhanden ist, warum eine zusätzliche Abfrage durchführen, um ihn zuerst zu erhalten? Ich könnte das tun:

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

Dies führt dazu, dass nur eine UPDATE-Anweisung ausgeführt wird.


Attach wird heutzutage auch in MVC verwendet, wenn Modelle direkt an EF zurückgegeben werden. Funktioniert ziemlich gut und spart eine Menge Codezeilen.
Piotr Kula

Antworten:


162

ObjectContext.AddObject und ObjectSet.AddObject :
Mit der AddObject- Methode können neu erstellte Objekte hinzugefügt werden, die nicht in der Datenbank vorhanden sind. Die Entität erhält einen automatisch generierten temporären EntityKey und ihr EntityState wird auf Added gesetzt . Wenn SaveChanges aufgerufen wird, wird dem EF klar, dass diese Entität in die Datenbank eingefügt werden muss.

ObjectContext.Attach und ObjectSet.Attach :
Auf der anderen Seite, Attach ist für Unternehmen verwendetdie bereits existieren in der Datenbank. Anstatt den EntityState auf "Hinzugefügt" zu setzen, führt "Anhängen" zu einem unveränderten EntityState. Dies bedeutet, dass er sich seit dem Anhängen an den Kontext nicht geändert hat. Es wird davon ausgegangen, dass Objekte, die Sie anhängen, in der Datenbank vorhanden sind. Wenn Sie die Objekte nach dem Anhängen ändern und SaveChanges aufrufen, wird der Wert des EntityKey verwendet, um die entsprechende Zeile zu aktualisieren (oder zu löschen), indem die entsprechende ID in der DB-Tabelle gefunden wird.

Darüber hinaus können Sie mit der Attach-Methode Beziehungen zwischen Entitäten definieren, die bereits im ObjectContext vorhanden sind, aber vorhanden sindwurde nicht automatisch verbunden. Grundsätzlich besteht der Hauptzweck von Anhängen darin, Entitäten zu verbinden, die bereits an den ObjectContext angehängt sind und nicht neu sind, sodass Sie Anhängen nicht zum Anhängen von Entitäten verwenden können, deren EntityState hinzugefügt wurde. In diesem Fallmüssen Sie Add () verwenden .

Angenommen, Ihre Personenentität verfügt über eine Navigationseigenschaft mit dem Namen Adressen, bei der es sich um eine Sammlung von Adressentitäten handelt . Angenommen, Sie haben beide Objekte aus dem Kontext gelesen, aber sie sind nicht miteinander verbunden, und Sie möchten es so machen:

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

Vielen Dank für die Antwort, ich verstehe die Definition der beiden (auch bekannt als die ersten beiden Absätze). Aber ich verstehe kein Szenario, in dem ich Attach verwenden muss. Ihr letzter Absatz macht für mich keinen Sinn (liest sich im Grunde wie eine Kombination der ersten beiden Absätze). Können Sie mir ein Beispiel geben, wo ich in meinem obigen Szenario "Anhängen" verwenden würde? Das ist wirklich das, wonach ich suche - Beispiele, keine Definitionen. Schätzen Sie Ihre Zeit wirklich. :)
RPM1984

1
Kein Problem, ich habe ein Code-Snippet hinzugefügt, um den letzten Absatz zu verdeutlichen, da Sie sehen können, dass wir zwei nicht verwandte Objekte haben, und Anhängen hilft uns, sie miteinander in Beziehung zu setzen. Das andere Beispiel wäre die Verwendung der Attach () -Methode, um eine "getrennte Entität" wieder an den Kontext
anzuhängen

1
Ja, ich habe jetzt. Ich habe gerade ein TechEd-Video auf EF4 (von Julie Lerman) gesehen, das ein Beispiel zeigt. Möglicherweise haben Sie eine Entität, die Sie NICHT aus einer Abfrage abgerufen haben (dh sie ist nicht verbunden), aber Sie wissen, dass sie vorhanden ist. Verwenden Sie daher Anhängen, um ein UPDATE für diese Entität durchzuführen. Sinnvoll, obwohl ich immer noch Schwierigkeiten habe, mir ein Szenario vorzustellen, in dem Sie eine "getrennte" Entität haben würden. Danke für Ihre Hilfe.
RPM1984

1
Toll. Kann ich Sie bitte bitten, den Link des Videos für andere Entwicklerkollegen zu teilen, die diesen Beitrag möglicherweise lesen?
Morteza Manavi

3
Der von RPM1984 gemeinsam genutzte Link ist fehlerhaft und wird jetzt zu channel9.msdn.com/Events/TechEd/NorthAmerica/2010/DEV205 umgeleitet . Viel Spaß
Gestapelt

31

Dies ist eine späte Antwort, kann aber anderen helfen, die dies finden.

Grundsätzlich kann eine "getrennte" Entität auftreten, wenn Sie eine Entität außerhalb des Bereichs "using" bearbeiten.

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
}

Wenn Sie einen anderen Bereich "using" eingeben, wird die Variable "e" getrennt, da sie zum vorherigen Bereich "using" gehört. Da der vorherige Bereich "using" zerstört wird, wird "e" getrennt.

So verstehe ich es.


3
Tchis Beispiel ist ein ausgezeichnetes und einfaches Beispiel - ja, die Employee-Variable sollte außerhalb deklariert werden. Versuchen Sie e.Address.Street außerhalb des Gültigkeitsbereichs und sehen Sie ein Popup-Fenster mit einer Nullreferenzausnahme. Wenn Sie Anhängen, muss die Anwendung im zweiten Bereich nicht zur Datenbank für den Mitarbeiter zurückkehren.
Steve

9

Dies ist ein Zitat aus Programming Entity Framework: DbContext

Wenn Sie Remove für eine Entität aufrufen, die nicht vom Kontext verfolgt wird, wird eine InvalidOperationException ausgelöst. Das Entity Framework löst diese Ausnahme aus, da nicht klar ist, ob es sich bei der zu entfernenden Entität um eine vorhandene Entität handelt, die zum Löschen markiert werden soll, oder um eine neue Entität, die einfach ignoriert werden soll. Aus diesem Grund können wir nicht einfach Entfernen verwenden, um eine nicht verbundene Entität als Gelöscht zu markieren. wir müssen es zuerst anhängen .

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

Die TestDeleteDestination-Methode simuliert eine Clientanwendung, die ein vorhandenes Ziel vom Server abruft und es dann an die DeleteDestination-Methode auf dem Server übergibt. Die DeleteDestination-Methode verwendet die Attach-Methode, um den Kontext darüber zu informieren, dass es sich um ein vorhandenes Ziel handelt. Anschließend wird mit der Methode Entfernen das vorhandene Ziel zum Löschen registriert


-8

Wie wäre es, wenn Sie sich nur auf den Primärschlüssel beziehen, anstatt ihn anzuhängen?

dh:

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();
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.