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!
Ich habe keine gute Antwort, aber ich kann mir vier "Lösungen" vorstellen, die wirklich Problemumgehungen sind:
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.
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.
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.
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%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.
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.
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.
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.
Tags und Links entity-framework table-per-type