Entity Framework 4, POCO-Objekte und ASP.Net MVC2. Ich habe viele zu viele Beziehungen, sagen wir zwischen BlogPost- und Tag-Entitäten. Dies bedeutet, dass ich in meiner von T4 generierten POCO BlogPost-Klasse Folgendes habe:
public virtual ICollection<Tag> Tags {
// getter and setter with the magic FixupCollection
}
private ICollection<Tag> _tags;
Ich fordere einen BlogPost und die zugehörigen Tags von einer Instanz des ObjectContext an und sende ihn an eine andere Ebene (Ansicht in der MVC-Anwendung). Später erhalte ich den aktualisierten BlogPost mit geänderten Eigenschaften und geänderten Beziehungen zurück. Zum Beispiel hatte es die Tags "A", "B" und "C", und die neuen Tags sind "C" und "D". In meinem speziellen Beispiel gibt es keine neuen Tags und die Eigenschaften der Tags ändern sich nie. Das einzige, was gespeichert werden sollte, sind die geänderten Beziehungen. Jetzt muss ich dies in einem anderen ObjectContext speichern. (Update: Jetzt habe ich versucht, in der gleichen Kontextinstanz zu tun und auch fehlgeschlagen.)
Das Problem: Ich kann die Beziehungen nicht richtig speichern. Ich habe alles versucht, was ich gefunden habe:
- Controller.UpdateModel und Controller.TryUpdateModel funktionieren nicht.
- Es funktioniert nicht, den alten BlogPost aus dem Kontext zu holen und dann die Sammlung zu ändern. (mit verschiedenen Methoden ab dem nächsten Punkt)
- Dies würde wahrscheinlich funktionieren, aber ich hoffe, dies ist nur eine Problemumgehung, nicht die Lösung :(.
- Versucht, Attach / Add / ChangeObjectState-Funktionen für BlogPost und / oder Tags in allen möglichen Kombinationen. Gescheitert.
- Das sieht so aus, wie ich es brauche, aber es funktioniert nicht (ich habe versucht, es zu beheben, kann es aber nicht für mein Problem).
- Versucht ChangeState / Add / Attach / ... die Beziehungsobjekte des Kontexts. Gescheitert.
"Funktioniert nicht" bedeutet in den meisten Fällen, dass ich an der angegebenen "Lösung" gearbeitet habe, bis sie keine Fehler mehr erzeugt und zumindest die Eigenschaften von BlogPost speichert. Was mit den Beziehungen passiert, ist unterschiedlich: Normalerweise werden Tags erneut mit neuen PKs zur Tag-Tabelle hinzugefügt, und der gespeicherte BlogPost verweist auf diese und nicht auf die ursprünglichen. Natürlich haben die zurückgegebenen Tags PKs, und vor den Speicher- / Aktualisierungsmethoden überprüfe ich die PKs und sie entsprechen denen in der Datenbank, sodass EF wahrscheinlich denkt, dass es sich um neue Objekte handelt und diese PKs die temporären sind.
Ein Problem, das ich kenne und das es möglicherweise unmöglich macht, eine automatisierte einfache Lösung zu finden: Wenn die Sammlung eines POCO-Objekts geändert wird, sollte dies durch die oben erwähnte Eigenschaft der virtuellen Sammlung geschehen, da der Trick FixCollection die umgekehrten Verweise am anderen Ende aktualisiert der Viele-zu-Viele-Beziehung. Wenn eine Ansicht jedoch ein aktualisiertes BlogPost-Objekt "zurückgibt", ist dies nicht geschehen. Dies bedeutet, dass es vielleicht keine einfache Lösung für mein Problem gibt, aber das würde mich sehr traurig machen und ich würde den EF4-POCO-MVC-Triumph hassen :(. Auch das würde bedeuten, dass EF dies in der MVC-Umgebung überhaupt nicht tun kann Es werden EF4-Objekttypen verwendet :(. Ich denke, die auf Snapshots basierende Änderungsverfolgung sollte herausfinden, dass der geänderte BlogPost Beziehungen zu Tags mit vorhandenen PKs hat.
Übrigens: Ich denke, das gleiche Problem tritt bei Eins-zu-Viele-Beziehungen auf (Google und mein Kollege sagen es). Ich werde es zu Hause versuchen, aber selbst wenn das funktioniert, hilft mir das in meinen sechs vielen-zu-vielen-Beziehungen in meiner App nicht weiter :(.
quelle
Antworten:
Versuchen wir es so:
Bearbeiten:
Ich denke, einer meiner Kommentare hat Ihnen die falsche Hoffnung gegeben, dass EF die Fusion für Sie durchführen wird. Ich habe viel mit diesem Problem gespielt und meine Schlussfolgerung besagt, dass EF dies nicht für Sie tun wird. Ich denke, Sie haben meine Frage auch auf MSDN gefunden . In Wirklichkeit gibt es im Internet viele solcher Fragen. Das Problem ist, dass nicht klar angegeben ist, wie mit diesem Szenario umgegangen werden soll. Schauen wir uns also das Problem an:
Problemhintergrund
EF muss Änderungen an Entitäten verfolgen, damit die Persistenz weiß, welche Datensätze aktualisiert, eingefügt oder gelöscht werden müssen. Das Problem ist, dass es in der Verantwortung von ObjectContext liegt, Änderungen zu verfolgen. ObjectContext kann Änderungen nur für angehängte Entitäten verfolgen. Entitäten, die außerhalb des ObjectContext erstellt werden, werden überhaupt nicht verfolgt.
Problembeschreibung
Basierend auf der obigen Beschreibung können wir klar sagen, dass EF besser für verbundene Szenarien geeignet ist, in denen die Entität immer an den Kontext gebunden ist - typisch für WinForm-Anwendungen. Webanwendungen erfordern ein getrenntes Szenario, in dem der Kontext geschlossen wird, nachdem die Anforderungsverarbeitung und der Entitätsinhalt als HTTP-Antwort an den Client übergeben wurden. Die nächste HTTP-Anforderung enthält geänderten Inhalt der Entität, der neu erstellt, an einen neuen Kontext angehängt und beibehalten werden muss. Erholung findet normalerweise außerhalb des Kontextbereichs statt (geschichtete Architektur mit beharrlicher Ignoranz).
Lösung
Wie soll man mit einem solchen Szenario ohne Verbindung umgehen? Bei der Verwendung von POCO-Klassen haben wir drei Möglichkeiten, mit der Änderungsverfolgung umzugehen:
Die manuelle Synchronisation auf einer einzelnen Entität ist eine einfache Aufgabe. Sie müssen nur eine Entität anhängen und AddObject zum Einfügen aufrufen, DeleteObject zum Löschen oder den Status in ObjectStateManager auf Modified zum Aktualisieren setzen. Der eigentliche Schmerz entsteht, wenn Sie sich mit Objektgraphen anstatt mit einzelnen Entitäten befassen müssen. Dieser Schmerz ist noch schlimmer, wenn Sie sich mit unabhängigen Assoziationen (solchen, die keine Fremdschlüsseleigenschaft verwenden) und vielen bis vielen Beziehungen befassen müssen. In diesem Fall müssen Sie jede Entität im Objektdiagramm, aber auch jede Beziehung im Objektdiagramm manuell synchronisieren.
Die manuelle Synchronisierung wird in der MSDN-Dokumentation als Lösung vorgeschlagen: Das Anhängen und Trennen von Objekten lautet:
Erwähnte Methoden sind ChangeObjectState und ChangeRelationshipState von ObjectStateManager = manuelle Änderungsverfolgung. Ein ähnlicher Vorschlag ist in einem anderen MSDN-Dokumentationsartikel enthalten: Definieren und Verwalten von Beziehungen lautet:
Darüber hinaus gibt es einen Blog-Beitrag zu EF v1, der genau dieses Verhalten von EF kritisiert.
Grund für die Lösung
EF hat viele „hilfreich“ Operationen und Einstellungen wie Refresh , laden , applyCurrentValues , ApplyOriginalValues , MergeOption etc. Aber durch meine Untersuchung all diese Funktionen nur für einzelne Unternehmen und wirkt sich nur skalare preperties (= nicht Navigation Eigenschaften und Beziehungen). Ich teste diese Methoden lieber nicht mit komplexen Typen, die in Entitäten verschachtelt sind.
Andere vorgeschlagene Lösung
Anstelle der echten Merge-Funktionalität bietet das EF-Team sogenannte Self Tracking Entities (STE) an, die das Problem nicht lösen. Zunächst funktioniert STE nur, wenn dieselbe Instanz für die gesamte Verarbeitung verwendet wird. In Webanwendungen ist dies nur dann der Fall, wenn Sie die Instanz im Ansichtsstatus oder in der Sitzung speichern. Aus diesem Grund bin ich sehr unglücklich über die Verwendung von EF und werde die Funktionen von NHibernate überprüfen. Die erste Beobachtung besagt, dass NHibernate möglicherweise eine solche Funktionalität hat .
Fazit
Ich werde diese Annahmen mit einem einzigen Link zu einer anderen verwandten Frage im MSDN-Forum abschließen. Überprüfen Sie die Antwort von Zeeshan Hirani. Er ist Autor von Entity Framework 4.0-Rezepten . Wenn er sagt, dass das automatische Zusammenführen von Objektgraphen nicht unterstützt wird, glaube ich ihm.
Trotzdem besteht die Möglichkeit, dass ich völlig falsch liege und in EF einige automatische Zusammenführungsfunktionen vorhanden sind.
Bearbeiten 2:
Wie Sie sehen, wurde dies bereits 2007 als Vorschlag zu MS Connect hinzugefügt . MS hat es als etwas geschlossen, das in der nächsten Version zu tun ist, aber tatsächlich wurde nichts unternommen, um diese Lücke zu verbessern, außer STE.
quelle
Ich habe eine Lösung für das oben von Ladislav beschriebene Problem. Ich habe eine Erweiterungsmethode für den DbContext erstellt, die automatisch das Hinzufügen / Aktualisieren / Löschen basierend auf einem Unterschied zwischen dem bereitgestellten Diagramm und dem persistierten Diagramm ausführt.
Bitte schauen Sie nach, ob dies hilfreich sein kann. Http://refactorthis.wordpress.com/2012/12/11/introducing-graphdiff-for-entity-framework-code-first-allowing-automated-updates-of-a- Graph-of-Detached-Entities /
Sie können direkt zum Code hier https://github.com/refactorthis/GraphDiff gehen
quelle
Ich weiß, dass es für das OP spät ist, aber da dies ein sehr häufiges Problem ist, habe ich dies veröffentlicht, falls es jemand anderem dient. Ich habe mit diesem Problem herumgespielt und ich denke, ich habe eine ziemlich einfache Lösung. Was ich tue, ist:
Im folgenden Beispiel sind "dataobj" und "_categories" die von meinem Controller empfangenen Parameter. "Dataobj" ist mein Hauptobjekt, und "_categories" ist eine IEnumerable, die die IDs der Kategorien enthält, die der Benutzer in der Ansicht ausgewählt hat.
Es funktioniert sogar für mehrere Beziehungen
quelle
Das Entity Framework-Team ist sich bewusst, dass dies ein Usability-Problem ist, und plant, es nach EF6 zu beheben.
Aus dem Entity Framework-Team:
Wenn dies Auswirkungen auf Sie hat, wählen Sie die Funktion unter
http://entityframework.codeplex.com/workitem/864
quelle
Alle Antworten waren großartig, um das Problem zu erklären, aber keine von ihnen hat das Problem wirklich für mich gelöst.
Ich stellte fest, dass alles gut funktionierte, wenn ich die Beziehung in der übergeordneten Entität nicht verwendete, sondern nur die untergeordneten Entitäten hinzufügte und entfernte.
Entschuldigung für die VB, aber genau darin ist das Projekt geschrieben, in dem ich arbeite.
Die übergeordnete Entität "Report" hat eine Eins-zu-Viele-Beziehung zu "ReportRole" und die Eigenschaft "ReportRoles". Die neuen Rollen werden durch eine durch Kommas getrennte Zeichenfolge aus einem Ajax-Aufruf übergeben.
In der ersten Zeile werden alle untergeordneten Entitäten entfernt. Wenn ich "report.ReportRoles.Remove (f)" anstelle von "db.ReportRoles.Remove (f)" verwenden würde, würde der Fehler angezeigt.
quelle