Objekt zu Objekt kopieren (mit Automapper?)

77

Ich habe ein klasse:

public class Person {
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

Ich habe zwei Instanzen von Person (Person1 und Person2). Ich möchte den Inhalt von person2 nach person1 kopieren. Ich möchte diese Kopie in einer Anweisung erstellen und nicht Eigenschaft für Eigenschaft:

person1.LastName = person2.LastName;

Im Dokument wird ein Objekt in ein anderes Objekt kopiert, der Typ ist jedoch unterschiedlich. Wie kann ich das Objekt kopieren, wenn der Typ identisch ist?

Kris-I
quelle
17
@Darin - Das würde eine Referenz erstellen, keine Kopie.
Steven Ryssaert
1
@Uw Concept, ja, aber da die Frage nicht sehr klar ist, dachte ich, ich könnte dies vorschlagen.
Darin Dimitrov
3
Ich möchte keine Referenz erstellen, sondern eine Kopie, die völlig unabhängig ist
Kris-I,
6
Ich schlage vor, AutoMapper dafür nicht zu verwenden - es wurde nicht zum Klonen von Elementen entwickelt (obwohl es in einigen Szenarien möglicherweise funktioniert). Stattdessen wirkt dieser BinaryFormatter-Trick magisch und lässt sich leicht in eine Erweiterungsmethode einkapseln.
Jimmy Bogard
3
Konzeptionell sind sie nicht dieselben Operationen. Das Klonen betrifft auch private, nicht nur öffentliche Daten. Beim Klonen werden grundsätzlich NUR private Felder betrachtet, beim Mapping jedoch nicht.
Jimmy Bogard

Antworten:

84

Soweit ich die Frage verstehe, möchte OP person2 nicht in eine neue Instanz von Person klonen , sondern fragt, wie der Inhalt von person2 in eine bereits vorhandene Instanz ( person1 ) von Person kopiert werden soll . Es gibt eine Überladung der Mapper.Map-Methode von AutoMapper, die dies für Sie erledigt:

Mapper.CreateMap<Person, Person>();
Mapper.Map<Person, Person>(person2, person1);
//This copies member content from person2 into the _existing_ person1 instance.

Anmerkung 1: Die Antwort von @ alexl erstellt eine neue Instanz von Person . Wenn Sie andere Verweise auf die Instanz haben, auf die person1 verweist, erhalten diese nicht die (vermutlich) gewünschte Datenaktualisierung, wenn Sie die Variable person1 auf eine neue Instanz umleiten .

Hinweis 2: Sie müssen sich bewusst sein, dass die (rekursive) Kopiertiefe davon abhängt, welche Zuordnungen AutoMapper zum Zeitpunkt der Zuordnung kennt!
Wenn ein Mitglied der Person Klasse sagen , die Klasse ist Gehirn , und Sie haben zusätzlich getan Mapper.CreateMap<Brain, Brain>();vor dem Kopieren von Daten Mapper.Map<Person, Person>(person2, person1);Anruf, dann person1 ihren gegenwärtigen halten Gehirn Instanz aber das Gehirn wird die Mitgliedswerte erhalten person2 ‚s Gehirn - Instanz. Das heißt, Sie haben eine tiefe Kopie .
Wenn AutoMapper jedoch vor dem Kopieren keine Brain-Brain-Zuordnung hat, ist das Gehirn von person1Das Mitglied verweist auf dieselbe Brain- Instanz wie die Person2 . Das heißt, Sie erhalten eine flache Kopie .
Dies gilt rekursiv für alle Mitglieder. Stellen Sie daher sicher, dass AutoMapper Zuordnungen für Mitgliedsklassen enthält, die Sie tief kopieren möchten, und keine Zuordnungen für Mitgliedsklassen, die Sie flach kopieren möchten.

Eine Alternative zur Verwendung von AutoMapper wäre die Verwendung eines Ansatzes mit Reflexion . (Beachten Sie, dass der Code im Link eine flache Kopie erstellt!)

"Unterstützung für das Füllen eines vorhandenen Objekts, anstatt dass AutoMapper das Zielobjekt selbst erstellt" wurde in AutoMapper Version 0.2 hinzugefügt .

Ulf Åkerstedt
quelle
Dies scheint auf Objektebene zu funktionieren, aber andere Objekte als Eigenschaften werden als Referenz kopiert. Vielleicht gibt es eine Möglichkeit, Automapper anzuweisen, die Eigenschaften zu klonen, anstatt eine Referenz zu kopieren?
Sonic Soul
30

Da Sie gefragt haben, With Automapper?kann ich vorschlagen, dass Sie AutoMapper nicht verwenden?

Verwenden Sie stattdessen MemberwiseClone()in einer CloneMethode, z

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public Person Clone()
    {
        return (Person) MemberwiseClone();
    }
}

AKTUALISIEREN

Es ist wichtig zu beachten, dass dies nicht den Wunsch der Originalplakate erfüllt, in die kopiert person1 werden soll person2

Die Verwendung (und wie @Jimmy Bogard betont) MemberwiseClone()wird jedoch bevorzugt, wenn Sie nur eine Kopie (Klon) des Objekts erstellen müssen .

Zum Beispiel, wenn Sie dies tun:

//I need a copy of person1 please! I'll make a new person object 
//and automapper everything into it!
var person2 = new Person2();
Mapper.Map<Person, Person>(person1, person2)

dann solltest / könntest du wirklich gebrauchen

//oh wait, i can just use this!
var person2 = person1.Clone()
wal
quelle
4
Das einzige Problem dabei ist, dass Sie jetzt angeben, dass Ihr Personentyp geklont werden kann. Dies ist semantisch falsch. Ziel ist es, eine Kopie (keinen Klon) von einer Instanz in eine andere Instanz durchzuführen.
Shaun Wilson
Können Sie bitte die Unterschiede in Bezug auf dieses spezielle Beispiel erläutern?
wal
6
wal: MemberwiseClone erstellt eine neue Instanz von Person. OP möchte die bereits vorhandene Instanz, auf die Person1 zeigt, beibehalten und mit den Werten von Person2 füllen. Möglicherweise gibt es andere Verweise auf die Instanz, auf die Person1 verweist und die nicht die gewünschte Datenaktualisierung erhalten, wenn Sie Person1 auf eine neue Instanz umleiten.
Ulf Åkerstedt
7
object.MemberwiseClone () führt eine flache Kopie durch, NICHT eine tiefe Kopie. google.ch/…
Errore Fatale
1
Sie haben nicht erklärt, warum Sie AutoMapper NICHT verwenden? Bin ich derjenige hier, der das return (Person) MemberwiseClone();für eine schlechte Praxis oder einfach für einen "Code-Geruch" hält?
Geka P
20
Mapper.CreateMap<Person, Person>();

// Perform mapping

var person1 = Mapper.Map<Person, Person>(person2);

Hoffe das hilft.

alexl
quelle
17
Sollte das nicht sein Mapper.Map<Person, Person>(person2, person1);? Ihr Weg wird ein neues Objekt erstellen person1(für das ich in meiner Antwort getötet werde;))
Wal
2
Für Leute, die hier googeln: Map.CreateMap wurde entfernt und die neue Methode zum Konfigurieren des Mappings wird hier beschrieben. Stackoverflow.com/a/38194308/4547594
Igand
Kopiert Automapper nur einfache Eigenschaften oder zusätzliche Navigationseigenschaften? Wie kann ich festlegen, dass nur die einfachen Eigenschaften und nicht die Objekteigenschaften kopiert werden sollen?
Naomi
2

Warum möchten Sie dafür Automapper verwenden? Ein einfacher Klon würde die Arbeit für Sie erledigen.

Lesen Sie hier mehr: Deep Cloning von Objekten

Boena
quelle
7
Weil AutoMapper eine Reflexion verwendet, die schneller ist als die binäre Serialisierung.
Huysentruitw
4
Für AutoMapper müssen nicht alle beteiligten Typen als [serialisierbar] gekennzeichnet sein. AutoMapper ist ebenfalls konfigurierbar. Wenn Sie nur einige der Felder kopieren oder eine Art Transformation als Teil der Kopie durchführen möchten, können Sie dies tun. :-)
Jonathan Gilbert
8
Ein "einfacher" tiefer Klon? Wir haben verschiedene Definitionen von einfach.
Gusdor
3
Mit @WouterHuysentruit Automapper können Sie das Mapping auch in Einheiten testen. Genial, wenn Sie den Typ später im Leben ändern.
Gusdor
2

In der aktuellen Version von AutoMapper können Sie die statische AutoMapper.Mapper.MapMethode nicht verwenden . Initialisieren Sie stattdessen einen neuen Mapper wie folgt:

var config = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<Person, Person>();
});

var mapper = new Mapper(config);

var clone = mapper.Map<Person>(person);

Normalerweise möchten Sie den Mapper in der Startup.csDatei für die Abhängigkeitsinjektion registrieren und in Ihre Business Class einfügen:

public void ConfigureServices(IServiceCollection services)
{
    var config = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Person, Person>();
    });

    var mapper = new Mapper(config);

    services.AddSingleton(mapper);

    // ...
}

Wichtig: Erstellen oder injizieren Sie den Mapper nicht in Ihrer Entitätsklasse!

Natürlich sollten Sie es vorziehen, MemberwiseClone()in einfachen Fällen zu verwenden.

MovGP0
quelle