Laden...

[erledigt] Entity Framework Klasse: 2 Objekte vergleichen und NUR Änderungen übernehmen

Erstellt von m.grauber vor 12 Jahren Letzter Beitrag vor 12 Jahren 2.512 Views
M
m.grauber Themenstarter:in
343 Beiträge seit 2010
vor 12 Jahren
[erledigt] Entity Framework Klasse: 2 Objekte vergleichen und NUR Änderungen übernehmen

Hallo,

ich kopiere flach eine Entity Klasse mit Klasse.Clone(), damit der Benutzer in Ruhe an der Klasse arbeiten kann, ohne dass sich die Original-Klasse ändert und die Änderungen auf Wunsch nicht übernommen werden müssen.

Die Übernahme habe ich mir so vorgestellt:
Ich möchte eine Klasse erstellen, die NUR alle Änderungen der geclonten Klasse auf die Originalklasse überträgt. Dazu müsste diese Methode nur meine Felder (die auch in der Datenbank vorhanden sind) prüfen und ggf. updaten, nicht aber die komplette Klasse kopieren (wie die meisten DeepCopy-Methoden). Nur wenn sich etwas geändert hat, sollte PropertyChanged ausgelöst werden.

Nun bin ich über viele DeepClone Möglichkeiten gestolpert, (teils auch über einen Constructor etc.). Ich benötige aber keine tiefe Kopie der kompeltten Klasse, sondern nur einzelner Felder bei Änderungen.

Auch hier gibt es bereits einige Interessante informationen, leider aber in eine andere Richtung: Kopie ohne ICloneable [oder warum man Objekte nicht kopieren sollte; Transaktionen auf Objekten].

Muss ich dies mit einer foreach-Schleife realisieren, oder gibt es da einen einfacheren Ansatz. Bei foreach habe ich die Schwierigkeit, dass es auf alle Klassen angewendet werden sollte. Wonach muss ich im Netz suchen?

P.S. Rollback o.ä. sind für mich nicht sinnvoll.

Danke!

Mfg
Michael

PS: Ich stelle nur Fragen, wenn ich in Büchern, im Web und in Foren nichts gefunden habe. Dumme Fragen bitte ich zu entschuldigen!

:] VISUAL STUDIO 2017 + .NET FRAMEWORK 4.5 + SQL-Server 2012 :]

F
10.010 Beiträge seit 2004
vor 12 Jahren

Du willst also IEditableObject implementieren !

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo m.grauber,

neben dem IEditableObject - für mich liest sich das auch so - liefert doch das EF mit dem Changetracking schon so ein Verhalten mit. Beim SaveChanges werden nur die geänderten Eigenschaften geupdated. Wenn nix geändert werden soll verwirf den Context.

Worrauf willst du hinaus?

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

M
m.grauber Themenstarter:in
343 Beiträge seit 2010
vor 12 Jahren

Hallo FZelle,

danke für die schnelle Antwort!

So viel wie ich dazu gelesen habe, kann IEditableObject per CancelEdit Änderungen rückgängig machen. (D. h. es arbeitet wie ein Rollback, was ich eigentlich aus folgenden Gründen vermeiden wollte:)

Mein Objekt befindet sich aber leider in einer oder mehreren ObservableCollections, die zu einem Zeitpunkt im Programmablauf existieren. Um nicht die Änderungen des Objekts in diesen ObservableCollections während meiner Bearbeitung durchzuführen, arbeite ich mit einem Klon.

IEditableObject (BeginEdit) würde in meinem Fall auch dieses Objekt in allen ObservableCollections beeinflussen. Das soll aber definitiv noch nicht passieren.

Ich benötige doch eher eine Methode mit folgenden Parametern:

public void UpdateObject(object Quellobjekt, object Zielobjekt)

Im Quellobjekt sollten dann alle in der DB vorhandenen Felder z. B. per foreach durchgegangen werden und wenn sich diese geändert haben, im Zielobjekt geändert werden. Dabei soll im Zielobjekt PropertyChanged ausgelöst werden.

Nur wie kann man das realisieren?

Danke

Mfg
Michael

PS: Ich stelle nur Fragen, wenn ich in Büchern, im Web und in Foren nichts gefunden habe. Dumme Fragen bitte ich zu entschuldigen!

:] VISUAL STUDIO 2017 + .NET FRAMEWORK 4.5 + SQL-Server 2012 :]

M
m.grauber Themenstarter:in
343 Beiträge seit 2010
vor 12 Jahren

Hallo Gü,

ja, nur SaveChanges schreibt die Änderung bereits in die Datenbank. Ich möchte aber an dieser Stelle noch nicht so weit gehen und die Daten nur in meiner ObservableCollection lokal auf dem Rechner "speichern".

Ich würde also nach meinem Clone() nur eine allgemeine Änderungsübernahme-Methode zurück zum Ursprung der Clone-Quelle benötigen.


...
Kunde tempkunde=(Kunde)kunde.Clone();
...
if (ok)
{
  UpdateObject(tempkunde, kunde);
}

Mfg
Michael

PS: Ich stelle nur Fragen, wenn ich in Büchern, im Web und in Foren nichts gefunden habe. Dumme Fragen bitte ich zu entschuldigen!

:] VISUAL STUDIO 2017 + .NET FRAMEWORK 4.5 + SQL-Server 2012 :]

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo m.grauber,

Nur wie kann man das realisieren?

Hol dir per Reflection die PropertyInfos und iteriere über diese und vergleiche die Werte bzw. aktualisiere sie.

Die PropertyInfos kannst du auch cachen (Stichwort: Dictionary<Type, PropertyInfos[]>) damit es schneller geht.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

M
m.grauber Themenstarter:in
343 Beiträge seit 2010
vor 12 Jahren

Hallo Gü,

Super!!! 👍 PropertyInfo war das Stichwort. Einziges Problem noch, wie kann ich nur meine wirklichen Felder (z. B. "Name", "Vorname", "ID" etc. herausfiltern?

Ich bekomme noch ziemlich viele andere Properties z. B. KundeProperty, EntityState etc...

Derzeit suche ich unter property.GetType(), habe aber noch nichts passendes gefunden.

propery.MemberType ist immer "Property" und kann nicht zur Unterscheidung herangezogen werden.

Grüße

Mfg
Michael

PS: Ich stelle nur Fragen, wenn ich in Büchern, im Web und in Foren nichts gefunden habe. Dumme Fragen bitte ich zu entschuldigen!

:] VISUAL STUDIO 2017 + .NET FRAMEWORK 4.5 + SQL-Server 2012 :]

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo m.grauber,

umgekeht: nicht deine Eigenschaften rausfiltern, sondern die Infrastruktur rausfiltern (filtern ist eigentlich immer etwas nicht erwünschten ausschließen -> hier für deine Eigenschaften wäre es ein Bandpass-Filter 😉).

Spontan fällt mir dazu ein dass die Assembly in welcher der Typ definiert wurde geprüft werden kann. Entity-Zeugs kommt ja alles von einer Assembly (System.Data.Entity.dll wenn ich mich nicht irre). Welche Eigenschaft/Methode dazu jedoch notwendig ist weiß ich nicht aus dem Stegreif, aber schau dir mal an was so alles bei PropertyInfo geboten wird. Am ehesten über GetGetMethod den Rückgabewert holen und dort die Assembly anschauen.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

M
m.grauber Themenstarter:in
343 Beiträge seit 2010
vor 12 Jahren

Hallo,

also ich habe nun ziemlich viel versucht. GetMembers(), GetProperties() und auch per PropertyDescriptor. Ich finde keinen Unterschied, an dem ich erkennen kann, ob es sich um ein Datenbanksfeld handelt. Alle Member sind auch vom Typ MethodInfo.

Vielleicht weiß noch jemand einen Rat.

Ansonsten funktioniert diese Methode wirklich ausgezeichnet! Vielen Dank!

Grüße

Mfg
Michael

PS: Ich stelle nur Fragen, wenn ich in Büchern, im Web und in Foren nichts gefunden habe. Dumme Fragen bitte ich zu entschuldigen!

:] VISUAL STUDIO 2017 + .NET FRAMEWORK 4.5 + SQL-Server 2012 :]

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo m.grauber,

meine obige Überlegung war schon korrekt, nur zum Schluss hab ich dich auf die falsche Fährte gelenkt. Daher bekommst du hier - als Entschädigung 😉 - Code geliefert.


public static class TypeExtensions
{
	public static IEnumerable<PropertyInfo> GetMyProperties(this Type type)
	{
		HashSet<Assembly> excludedAssemblies = new HashSet<Assembly>();
		excludedAssemblies.Add(typeof(ObjectContext).Assembly);

		foreach (PropertyInfo pi in type.GetProperties())
		{
			Assembly declaringAssembly = pi.DeclaringType.Assembly;
			if (!excludedAssemblies.Contains(declaringAssembly))
				yield return pi;
		}
	}
}

Das HashSet<Assembly> hab ich nur deshalb damit auch andere Assemblies ausgeschlossen werden können. Die Erweiterungsmethode könnte auch auf LinQ umgeschrieben werden.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

M
m.grauber Themenstarter:in
343 Beiträge seit 2010
vor 12 Jahren

Hallo Gü,

ich war im Kurzurlaub und kann mich erst jetzt melden.

Vielen Dank für den Code gleich als IEnumerable und ExtensionMethod! Wer diesen Code nutzt, muss allerdings berücksichtigen, dass z. B. folgende Verweise auf andere Tabellen und Referenzen hier noch mit enthalten sind, da diese ja auch im ObjectContext vorkommen:

  • MyProject.Tabelle1 Tabelle1
  • System.Data.Objects.DataClasses.EntityReference`1[MyProject.Tabelle2] Tabelle2Reference

Ich frage nun die Felder daher zusätzlich nochmals einzeln ab und das funktioniert auch.

Vielen Dank! 👍

Grüße

Mfg
Michael

PS: Ich stelle nur Fragen, wenn ich in Büchern, im Web und in Foren nichts gefunden habe. Dumme Fragen bitte ich zu entschuldigen!

:] VISUAL STUDIO 2017 + .NET FRAMEWORK 4.5 + SQL-Server 2012 :]