2010-02-02 18 views
48

Ich erstelle Software, mit der Benutzer neue Produkte basierend auf älteren Produkten erstellen können.Klonen von Daten auf Entity Framework

Jetzt muss ich kopieren/klonen Operationen mit Entity Framework. Zuerst fing ich an, so zu schreiben:

Problem ist, dass dies nicht ein netter Weg, es zu tun. Gibt es irgendwelche einfachen Kloninformationen (außer Guid, die für neue Zeilen generiert werden müssen) oder sollte ich manuell alles kopieren?

Andere Lösung

Sie auch EmitMapper oder AutoMapper zu tun Kopieren der Eigenschaften nutzen könnten.

Antwort

15

gerade Serialisierung verwenden, können Sie dies tun:

http://social.msdn.microsoft.com/forums/en-US/adodotnetentityframework/thread/a967b44b-c85c-4afd-a499-f6ff604e2139/

Reflexion verwenden, aber mit mehr Code viel Sie dies tun können: http://msmvps.com/blogs/matthieu/archive/2008/05/31/entity-cloner.aspx

+0

Es scheint, dass es nicht schöner Lösung ist, so werde ich dies als eine Antwort akzeptieren :) – Tx3

+0

@ Tx3 Wenn eine der Antworten später die mehr von der Nettigkeit bieten hinzugefügt Sie suchen, fühlen sich frei, um die akzeptierte Antwort zu aktualisieren. :-) –

+0

Die hier angebotene Lösung (http://stackoverflow.com/a/15322430/159341) fühlt sich an wie eine viel sauberere Lösung. Verwenden von 'AsNoTracking()', um ein losgelöstes Objekt zurückzugeben, das erneut hinzugefügt werden kann, wodurch beim Speichern des Kontexts eine neue Entität erstellt wird. – Tr1stan

61

einer Entität in Entity Framework So klonen könnten Sie Trennen Sie einfach die Entität von der DataContext und fügen Sie sie erneut zur EntityCollection hinzu.

context.Detach(entity); 
entityCollection.Add(entity); 

aktualisieren für EF6 + (von Kommentaren)

context.Entry(entity).State = EntityState.Detached; 
entity.id = 0; 
entity.property = value; 
context.Entry(entity).State = EntityState.Added; 
context.SaveChanges(); 
+3

Für diejenigen, die diese Methode ausprobieren. Denken Sie daran, dass Sie keine neue ID (Schlüsselwert) erhalten, bis Sie context.SaveChanges() aufrufen. – Rabbi

+0

Gute Sachen, danke :-) –

+24

Das einzige Problem dabei ist, wenn Sie eine Entity lösen, verlieren Sie alle Referenzen. – Martin

9
public static EntityObject Clone(this EntityObject Entity) 
{ 
    var Type = Entity.GetType(); 
    var Clone = Activator.CreateInstance(Type); 

    foreach (var Property in Type.GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.SetProperty)) 
    { 
     if (Property.PropertyType.IsGenericType && Property.PropertyType.GetGenericTypeDefinition() == typeof(EntityReference<>)) continue; 
     if (Property.PropertyType.IsGenericType && Property.PropertyType.GetGenericTypeDefinition() == typeof(EntityCollection<>)) continue; 
     if (Property.PropertyType.IsSubclassOf(typeof(EntityObject))) continue; 

     if (Property.CanWrite) 
     { 
      Property.SetValue(Clone, Property.GetValue(Entity, null), null); 
     } 
    } 

    return (EntityObject)Clone; 
} 

Dies ist eine einfache Methode, die ich geschrieben habe. Es sollte für die meisten Leute funktionieren.

+0

Das funktioniert wirklich gut für modale Dialoge. Sie klonen das Objekt, legen es als den Kontext für den modalen Dialog fest und wenn sie dann auf OK klicken, setzen Sie die Werte zurück auf das ursprüngliche Objekt. Wenn sie absagen, tust du nichts. Sehr leicht. Zuvor habe ich die Änderungen rückgängig gemacht, aber das wurde unordentlich, wenn das Objekt bereits Änderungen hatte, so dass es direkt zum Start rückgängig gemacht würde. Dies behebt auch das Problem, dass die Bindung im Hauptfenster aktualisiert wird, wenn der Benutzer das Dialogfeld eingibt. – MikeKulls

+5

Dies erfordert eine Reihe von Korrekturen. Die erste ist, ich denke, Sie wollten fortfahren statt Pause für die ersten drei Zeilen in der foreach-Anweisung verwenden. Abhängig von der Reihenfolge der zurückgegebenen Eigenschaften verhält sich das falsch. Die zweite ist, dass Sie BindingFlags.DeclaredOnly entfernen müssen, da dies dazu führt, dass es mit Entitätsobjekten fehlschlägt, die von anderen Entitätsobjekten geerbt werden. In meinem Fall wird Staff beispielsweise von Person geerbt, aber ich habe gerade die Eigenschaften von Staff geklont. – MikeKulls

+0

Das verliert auch die Referenzen? –

8

Um eine neue Zeile, deren Inhalt basiert auf einer vorhandenen Zeile folgendermaßen vor, hinzuzufügen:

  1. ein Unternehmen Holen Sie sich auf dem Startreihe basiert.
  2. Legen Sie den Eintragsstatus für die Entität auf Added fest.
  3. Ändern Sie die Entität.
  4. Änderungen speichern.

Hier ist ein Beispiel:

var rabbit = db.Rabbits.First(r => r.Name == "Hopper"); 
db.Entry(rabbit).State = EntityState.Added; 
rabbit.IsFlop = false; 
db.SaveChanges(); 
+0

Als ich das versuchte, bekam ich eine Fehlermeldung, dass "Die Änderungen an der Datenbank wurden erfolgreich festgeschrieben, aber ein Fehler trat beim Aktualisieren des Objektkontextes auf." – Rocklan

0

Wenn Sie eine Kopie eines Unternehmens zum Vergleich erstellen möchten später in Ihrem Code-Ausführung, können Sie die Einheit in einem neuen db Kontext auswählen.

Wenn Sie zum Beispiel eine Einheit aktualisieren, dann später im Code möchten, dass Sie die aktualisierte und ursprüngliche Unternehmen vergleichen:

var db = new dbEntityContext(); 
var dbOrig = new dbEntityContext(); 

var myEntity = db.tblData.FirstOrDefault(t => t.Id == 123); 
var myEntityOrig = dbOrig.tblData.FirstOrDefault(t => t.Id == 123); 

//Update the entity with changes 
myEntity.FirstName = "Gary"; 

//Save Changes 
db.SaveChnages(); 

An diesem Punkt wird myEntity.FirstName enthalten "Gary" während myEntityOrig.FirstName den ursprünglichen Wert enthält . Nützlich, wenn Sie eine Funktion zum Protokollieren von Änderungen haben, bei der Sie die aktualisierte und ursprüngliche Entität übergeben können.

0

Eine wirklich kurze Art der Duplizierung von Entitäten mit Generika (VB, sorry).
Es kopiert Fremdschlüsselwerte (externe IDs), lädt jedoch nicht die zugehörigen Objekte.

<Extension> _ 
Public Function DuplicateEntity(Of T As {New, Class})(ctx As myContext, ent As T) As T 
    Dim other As New T 'T is a proxy type, but New T creates a non proxy instance 
    ctx.Entry(other).State = EntityState.Added 'attaches it to ctx 
    ctx.Entry(other).CurrentValues.SetValues(ent) 'copies primitive properties 
    Return other 
End Function 

Zum Beispiel:

newDad = ctx.DuplicateEntity(oDad) 
newDad.RIDGrandpa ' int value copied 
newDad.Grandpa ' object for RIDGrandpa above, equals Nothing(null) 
newDad.Children ' nothing, empty 

ich nicht genau weiß, wie Grandpa in diesem Fall erneut zu laden.
funktioniert das nicht:

ctx.SaveChanges() 
ctx.Entry(newDad).Reload() 

aber wirklich, kein Problem. Ich würde lieber Grandpa von Hand zuweisen, wenn ich es brauche.

newDad.Grandpa = oDad.Grandpa 

EDIT: Als MattW proposes in his comment, Abnehmen und die Suche nach der neuen Einheit Sie ihre Kinder bekommen geladen (nicht Sammlungen).

ctx.Entry(newDad).State = EntityState.Detached 
ctx.Find(newDad.RowId) 'you have to know the key name 
+0

Perfekt ... Ich habe gerade eine Web-API-Put-Methode geschrieben und wollte überprüfen, dass einige Schlüsselfelder nicht von vorhandenen Werten geändert wurden - ich habe die Entität aus der Datenbank geladen, die unveränderlichen Felder mit dem Wert aus dem Web verglichen API, kopieren Sie den Rest und speichern Sie die Datenbankkopie. SetValues ​​ist genau das, wonach ich gesucht habe, um den Schritt "Kopiere den Rest über" auszuführen. – MattW

+1

Haben Sie beim Nachladen ein 'Detach' gefolgt von' Find' versucht? Ich erwarte, dass es eine neue Objektreferenz geben würde, weiß nicht, ob das ein Problem für Sie wäre. – MattW

+0

ja! es scheint zu funktionieren. Ich bearbeite meine Antwort. Vielen Dank –

Verwandte Themen