Entity Framework 5 Aktualisierung per Tabelle-pro-Typ, Untertyp ändern, aber den gleichen Basistyp beibehalten

8

Ich habe eine einfache Hierarchie

%Vor%

Ich plane, die Tabelle-für-Typ-Hierarchie für meine DB zu verwenden. Also werden 3 Tabellen erstellt, eine Basis und zwei untergeordnete, die dieselbe PK wie die Basis verwenden.

Mein Problem ist, dass ich einen CommunicationSupport aktualisieren möchte, indem ich seinen Typ ändere. Nehmen wir an, ich erstelle einen TelecomSupport, speichere ihn und ändere seinen Typ in einen PostalSupport und speichere ihn erneut (Update). Das Ergebnis, das ich erwarte, ist, dass EF den gleichen Basisdatensatz (CommunicationSupport Id) behält, aber den Datensatz in der TelecomSupport-Tabelle löscht und einen neuen im PostalSupport erstellt. Daher sind TelecomSupport und PostalSupport exklusiv und können nicht den gleichen Basis-CommunicationSupport nutzen.

Wie kann ich das mit EntityFramework 5 machen?

Danke für Ihre Hilfe!

    
Fab 07.08.2013, 10:21
quelle

1 Antwort

6

Ich habe keine gute Antwort, aber ich kann mir vier "Lösungen" vorstellen, die wirklich Problemumgehungen sind:

  1. Verwenden Sie keine von DBMS berechneten Werte für Ihre Primärschlüssel (wenn Sie bereits natürliche Schlüssel verwenden, ist das in Ordnung).
  2. Verwenden Sie DBMS-berechnete Ersatz Schlüssel.
  3. Folgen Sie etwa dem Statusmuster .
  4. Mach etwas böses Voodoo mit dem Objektstatus-Manager .

Update: Es scheint einen weitverbreiteten Konsens zu geben, dass sich das Versuchen nicht einmal lohnt; Die meisten Menschen verwenden daher einfach gespeicherte Prozeduren, um das Problem zu umgehen.

Verwenden von natürlichen Schlüsseln

Denken Sie zunächst daran, dass die Objekte, die von der EF verfolgt werden, Teil Ihrer DAL sind und nicht Ihr Domänenmodell (unabhängig davon, ob Sie POCOs verwenden oder nicht). Einige Leute brauchen kein Domänenmodell, aber denken Sie daran, da wir uns diese Objekte nun als Repräsentationen von Tabellensätzen vorstellen können, die wir so manipulieren, wie wir es mit Domänenobjekten nicht tun würden.

Hier verwenden wir IDbSet.Remove , um die Datensätze der Entität zu löschen und dann hinzuzufügen neue mit demselben Primärschlüssel, die IDbSet.Add verwenden, alles in einer einzigen Transaktion. Sehen Sie sich die ChangeType -Methode im folgenden Beispielcode an.

Theoretisch ist Integrität in Ordnung, und in der Theorie könnte EF feststellen, was Sie zu tun versuchen und Dinge optimieren. In der Praxis ist es derzeit nicht (Ich habe die SQL-Schnittstelle profiliert, um dies zu überprüfen). Das Ergebnis ist, dass es hässlich aussieht ( DELETE + INSERT anstelle von UPDATE ), wenn also Schönheit und Leistung des Systems Probleme sind, ist es wahrscheinlich ein No-Go. Wenn Sie es nehmen können, ist es relativ einfach.

Hier ist ein Beispielcode, den ich verwendet habe, um dies zu testen (wenn Sie experimentieren möchten, erstellen Sie einfach eine neue Konsolenanwendung, fügen Sie einen Verweis auf die Assembly EntityFramework hinzu und fügen Sie den Code ein).

A ist die Basisklasse, X und Y sind Unterklassen. Wir betrachten Id als einen natürlichen Schlüssel, so dass wir es in die Kopiekonstruktoren der Unterklassen kopieren können (hier nur für Y implementiert). Der Code erstellt eine Datenbank und sät diese mit einem Datensatz vom Typ X . Dann wird es ausgeführt und ändert seinen Typ in Y , wodurch offensichtlich X -spezifische Daten verloren gehen. Der Kopierkonstruktor ist der Ort, an dem Sie Daten transformieren oder archivieren würden, wenn Datenverlust nicht Teil des Geschäftsprozesses ist. Der einzige "interessante" Code ist die ChangeType -Methode, der Rest ist boilerplate.

%Vor%

Ausgabe:

%Vor%

Hinweis: Wenn Sie einen automatisch generierten natürlichen Schlüssel möchten, können Sie EF nicht bitten, das DBMS zu berechnen, oder EF wird verhindern, dass Sie ihn so manipulieren, wie Sie möchten (siehe unten) ). In der Tat behandelt EF alle Schlüssel mit berechneten Werten als Ersatzschlüssel, obwohl sie diese immer noch glücklich verliert (das Schlechte beider Welten).

Hinweis: Ich notiere die Unterklassen mit Table , weil Sie ein TPT-Setup erwähnt haben, das Problem jedoch nicht mit TPT zusammenhängt.

Ersatzschlüssel verwenden

Wenn Sie davon ausgehen, dass ein Ersatzschlüssel wirklich intern ist , dann ist es egal, ob er sich unter Ihrer Nase verändert, solange Sie weiterhin auf Ihre Daten zugreifen können (mit einem sekundären Index) zum Beispiel).

Hinweis: In der Praxis verlieren viele Personen Ersatzschlüssel (Domänenmodell, Serviceschnittstelle, ...). Tu es nicht.

Wenn Sie das vorherige Beispiel nehmen, entfernen Sie einfach das Attribut DatabaseGenerated und die Zuordnung von Id im Kopierkonstruktor der Subtypen.

Hinweis: Mit dem vom DBMS generierten Wert wird die Eigenschaft Id von EF vollständig ignoriert und dient nur dem Zweck, vom Modellerzeuger analysiert zu werden, um den Prozentsatz zu generieren. Spalte "co_de%" im SQL-Schema. Das und von schlechten Programmierern durchgesickert zu werden.

Ausgabe:

%Vor%

Verwenden des Zustandsmusters (oder ähnlich)

Diese Lösung wird wahrscheinlich von den meisten Menschen als "richtige Lösung" betrachtet, da Sie den eigentlichen Typ eines Objekts in den meisten objektorientierten Sprachen nicht ändern können. Dies ist der Fall für CTS -konforme Sprachen, einschließlich C #.

Das Problem besteht darin, dass dieses Muster in einem Domänenmodell und nicht in einer DAL, die mit EF implementiert ist, ordnungsgemäß verwendet wird. Ich sage nicht, dass es unmöglich ist, Sie können Dinge mit komplexen Typen oder TPH-Konstrukten hacken, um die Schaffung eines Zwischentisches zu vermeiden, aber höchstwahrscheinlich schwimmen Sie den Fluss hinauf, bis Sie aufgeben. Hoffentlich kann jemand mich aber falsch beweisen.

Hinweis: Sie können entscheiden, dass Ihr relationales Modell anders aussehen soll. In diesem Fall können Sie dieses Problem umgehen. Es wäre aber keine Antwort auf deine Frage.

Verwendung des internen EF voodoo

Ich habe mich ziemlich schnell in der Referenzdokumentation für Id , DbContext und ObjectContext , und ich kann nicht sofort irgendeine Möglichkeit finden, den Typ einer Entität zu ändern . Wenn Sie mehr Glück als ich haben, können Sie DTOs und verwenden ObjectStateManager , um die Konvertierung durchzuführen.

Wichtiger Hinweis

Bei den ersten beiden Problemumgehungen werden Sie wahrscheinlich eine Reihe von Problemen mit Navigationseigenschaften und Fremdschlüsseln (wegen der Operation DbPropertyValues + DELETE ) haben. Dies wäre eine separate Frage.

Fazit

EF ist nicht so flexibel, wenn Sie etwas Nicht-Triviales tun, aber es verbessert sich ständig. Hoffentlich wird diese Antwort in Zukunft nicht relevant sein. Es ist auch möglich, dass mir ein existierendes Killer-Feature nicht bekannt ist, das das macht, was Sie wollen, also treffen Sie keine Entscheidungen, die auf dieser Antwort basieren.

    
tne 07.08.2013 13:36
quelle