Laden...

EF - CodeFirst: Mehr als 2 one-to-one-Beziehungen

Erstellt von Paschulke vor 11 Jahren Letzter Beitrag vor 11 Jahren 1.939 Views
P
Paschulke Themenstarter:in
69 Beiträge seit 2011
vor 11 Jahren
EF - CodeFirst: Mehr als 2 one-to-one-Beziehungen

verwendetes Datenbanksystem: SQL Server 2008 R2

Hallo,

ich baue gerade ein Beispielprojekt, um CodeFirst kennen zu lernen.

Das Beispielprojekt verwaltet u. a. die Spieltage einer Bundesligasaison. Zu jedem Spieltag sollen 3 Tabellen gespeichert werden: Heimtabelle, Auswärtstabelle und Gesamttabelle.

Ich habe also eine Entität Matchday mit (u. a.) den 3 folgenden Properties:


public virtual Ranking CompleteRanking { get; set; }
public virtual Ranking HomeRanking { get; set; }
public virtual Ranking AwayRanking { get; set; }

Ranking (u. a.) sieht so aus:

public virtual Matchday Matchday { get; set; }

RankingConfiguration sieht (u. a.) so aus:


HasRequired(r => r.Matchday).WithOptional(md => md.CompleteRanking);
HasRequired(r => r.Matchday).WithOptional(md => md.HomeRanking);
HasRequired(r => r.Matchday).WithOptional(md => md.AwayRanking);

Folgendes Problem:
Das EntityFramework legt eine Matchdays-Tabelle mit den Fremdschlüsseln "CompleteRanking_Id" und "HomeRanking_Id" an.
Der dritte Fremdschlüssel ("AwayRanking_Id") wird nicht angelegt. Tausche ich in der Konfiguration die Reihenfolge (z. B. Home vor Away), wird jeweils die zuletzt konfigurierte Beziehung ignoriert.

Es scheint so, als könne das EntityFramework lediglich 2 one-to-one Beziehungen zu einer Tabelle verwalten. Kann das sein? Oder muss ich irgend etwas beachten?

Viele Grüße
Uli

16.827 Beiträge seit 2008
vor 11 Jahren

Das funktioniert auch so nicht. Wenn Du Dich mal über die Funktiosweise informierst, wirst Du dies auch nachvollziehen können.

Das was Du machst ist eine 1-3 Bezeichung - sowas gibts halt nicht.
Eine One-To-One-Beziehung braucht auf jeder Seite sein eigenes Property, da es natürlich auch eigene Relationen sind. Du versuchst hier aber quasi drei Beziehungen auf eine Property zu mappen 😉

Gruß

P
Paschulke Themenstarter:in
69 Beiträge seit 2011
vor 11 Jahren

Sicher, dass das nicht geht? Möglicherweise heißt das nicht mehr "one-to-one". Das will ich nicht bestreiten. Aber dass ich auch in der Ranking-Entität 3 Properties benötige, kann ich nicht glauben.

Ich bin gerade bei der Suche nach einer Lösung auf folgenden Artikel gestoßen:
Associations in EF Code First: Part 5 – One-to-One Foreign Key Associations

Die dort beschriebene Lösung funktioniert bei mir allerdings auch (noch) nicht.

Dennoch: Es sieht demnach so aus, als wäre mein Ansatz prinzipiell nicht verkehrt!?

16.827 Beiträge seit 2008
vor 11 Jahren

Ja. Definitiv nicht. Ich finde zwischen Deinem Code und dem Beispiel auch keine Verbindung. Möglicherweise meinst Du etwas, was aktuell nicht ganz so klar ist. Vielleicht erläuterst Du das noch mal, was Du überhaupt tun willst. Wahrscheinlich stimmt die Modell-Logik nicht.

Damit Deine Navigation-Properties gültig sind:


public virtual Matchday MatchdayComplete { get; set; }
public virtual Matchday MatchdayHome { get; set; }
public virtual Matchday MatchdayAway { get; set; }

HasRequired(r => r.MatchdayComplete ).WithOptional(md => md.CompleteRanking);
HasRequired(r => r.MatchdayHome ).WithOptional(md => md.HomeRanking);
HasRequired(r => r.MatchdayAway ).WithOptional(md => md.AwayRanking);

Das EF hat im Hintergrund nichts anderes als eine relationale Datenbank - und im Prinzip auch nur deren Features.
Es gibt 1-1, 1-n, n-n Beziehungen.

1.378 Beiträge seit 2006
vor 11 Jahren

Entweder du hast 3x 1:1 Beziehungen, dann brauchst du auf beiden Seiten jeweils 3 Properties um entsprechend navigieren zu können. Oder du realisierst es als 1:n Beziehung und hast dann auf beiden Seiten nur eine Property. Auf der "1" Seite eine Collection und die Info ob Auswärts, Heimwärts usw. müsstest du dann ins Ranking einbauen.

Lg, XXX

P
Paschulke Themenstarter:in
69 Beiträge seit 2011
vor 11 Jahren

Abt, kurz zur Klarstellung, damit Du nicht von falschem Code ausgehst:
Dein Zitat entspricht nicht meinem Code! Bei mir heißt es jeweils: "HasRequired(r => r.Matchday).WithOptional(...)".

Die Verbindung zum genannten Beispiel:
In dem Beispiel hat ein User 2 Adressen (BillingAddress und DeliveryAddress).
In meinem Beispiel habe ich dieselbe Art von Relation zwischen Matchday und Ranking. Also anstatt BillingAdress und DeliveryAdress habe ich CompleteRanking, HomeRanking und AwayRanking).

Das Ergebnis sollte in der DB wie folgt aussehen:
Tabelle Rankings:

  • Id (PS, bigint, NICHT NULL)
  • KindOfRanking (int, NICHT NULL) <-- In der Entität ist das eine Enumeration (Complete, Home, Away).
  • Matchday_id (bigint, NICHT NULL) <-- Das scheint das Problem zu sein, wenn ich es wie im Beispiel mache. EF legt hier immer einen FS an. Ich will aber lediglich die Information speichern.)

Tabelle Matchdays:

  • Id (PS, bigint, NICHT NULL)
  • Number (int, NICHT NULL)
  • CompleteRanking_Id(FS, bigint, NULL)
  • HomeRanking_Id(FS, bigint, NULL)
  • AwayRanking_Id(FS, bigint, NULL) <-- Fehlt bei mir!
  • League_Id (FS, bigint, NOT NULL)

Es sieht bei mir auch genau so aus. Lediglich die Spalte AwayRanking wird von EF nicht angelegt.

16.827 Beiträge seit 2008
vor 11 Jahren

Du kannst Dich drehen wie Du willst. Mit nur einer Property funktioniert das nicht. Akzeptiers oder leb mit dem Fehler. 😉

Und nein, Dein Code ist NICHT vergleichbar mit dem Beispiel.
Im Beispiel sind **ZWEI **PROPERTIES für **ZWEI **RELATIONEN.
Du hast EINE PROPERTY für DREI RELATIONEN. Fällt Dir was auf?

Deswegen -> für jede Beziehung nen eigenes Property DEFINIEREN.
Wenn dahinter die gleiche ID steckt ist das absolut in Ordnung, aber die Modellierung ist falsch.

Daher:


public virtual Matchday MatchdayComplete { get; set; }
public virtual Matchday MatchdayHome { get; set; }
public virtual Matchday MatchdayAway { get; set; }

HasRequired(r => r.MatchdayComplete ).WithOptional(md => md.CompleteRanking);
HasRequired(r => r.MatchdayHome ).WithOptional(md => md.HomeRanking);
HasRequired(r => r.MatchdayAway ).WithOptional(md => md.AwayRanking);

Falls Dich das Beispiel verwirrt, so ist dieses nicht unbedingt optimal.
Du kannst nämlich von dem User auf Address navigieren, aber nicht von Address auf User, da keine Property festgelegt ist.

Damit das Beispiel vollständig ist:

public class User
{
    public int UserId { get; set; }
    public string Name { get; set; }
    public int BillingAddressId { get; set; }
    public int DeliveryAddressId { get; set; }
        
    public Address BillingAddress { get; set; }
    public Address DeliveryAddress { get; set; }
}
 
public class Address
{
    public int AddressId { get; set; }
    public string Street { get; set; }
    public string City { get; set; }
    public string ZipCode { get; set; }

   public User BillingUser { get; set;}
   public User DeliveryUser { get; set;}
}

Daher modelliert man in diesem Fall eine Basisklasse Address; nutzt aber noch die Klassen "DeliveryAddress" und "BillingAddress", um die Relationen zu Formen - und, dass unnötig optionale Properties - wie hier in diesem Beispiel - wegfallen.

Du kannst durchaus eine Property hier weglassen, aber dann ist ein Navigieren aus beiden Seiten nicht möglich.

P
Paschulke Themenstarter:in
69 Beiträge seit 2011
vor 11 Jahren

OK, ich gebe mich geschlagen.

Aber eins würde mich noch interessieren:
Habe ich komplett falsch gedacht? Oder funktioniert meine Lösung lediglich wegen des "Multiple Cascade Path"-Problems des SQL Servers nicht (darauf wird in dem von mir genannten Artikel ebenfalls hingewiesen)?

Auf alle Fälle schon einmal: Vielen Dank für die Hilfe und die Mühe 👍

16.827 Beiträge seit 2008
vor 11 Jahren

Das ist bei Dir ein Denkfehler; wahrscheinlich aufgrund des Modells. Aber außer MatchDay und Ranking seh ich hier nichts. Und daraus dann was logisches zu stricken fällt schwer.

1-1 Beziehungen nutzt man im Beispiel der Adresse, wenn Du EINE Rechnungsadresse und EINE Versandadresse für den Kunden verfügbar sein soll.
Bei Amazon würde aber zum Beispiel eine 1-n Beziehung existieren (sofern diese relationale Datenbanken nutzen würden, was sie nicht tun), da man mehrere Versandadressen pflegen kann; diese aber nur für einen Benutzer verfügbar sein soll.

Das Problem, was Du hier hast, wäre nicht aufgetreten, wenn Du Dich informiert hättest, was das EF denn im Hintergrund macht: da steckt einfach nur ne relationale Datenbank dahinter. Und auch in PlainSQL - ohne OR-Mapper - würde Dein Vorhaben nicht funktionieren.