wake-up-neo.net

Optionale Eins-zu-Eins-Beziehung mit Entity Framework Fluent API

Wir möchten eine optionale 1: 1-Beziehung mit Entity Framework Code First verwenden. Wir haben zwei Entitäten.

public class PIIUser
{
    public int Id { get; set; }

    public int? LoyaltyUserDetailId { get; set; }
    public LoyaltyUserDetail LoyaltyUserDetail { get; set; }
}

public class LoyaltyUserDetail
{
    public int Id { get; set; }
    public double? AvailablePoints { get; set; }

    public int PIIUserId { get; set; }
    public PIIUser PIIUser { get; set; }
}

PIIUser kann eine LoyaltyUserDetail haben, aber LoyaltyUserDetail muss eine PIIUser..__ haben.

modelBuilder.Entity<PIIUser>()
            .HasOptional(t => t.LoyaltyUserDetail)
            .WithOptionalPrincipal(t => t.PIIUser)
            .WillCascadeOnDelete(true);

Dieser Ansatz hat keinen LoyaltyUserDetailId-Fremdschlüssel in der PIIUsers-Tabelle erstellt.

Danach haben wir den folgenden Code ausprobiert.

modelBuilder.Entity<LoyaltyUserDetail>()
            .HasRequired(t => t.PIIUser)
            .WithRequiredDependent(t => t.LoyaltyUserDetail);

Diesmal hat EF in diesen beiden Tabellen jedoch keine Fremdschlüssel erstellt.

Haben Sie Ideen für dieses Problem? Wie können wir eine eins zu eins optionale Beziehung unter Verwendung des fließenden APIs des Entity Frameworks erstellen?

65
İlkay İlknur

EF Code First unterstützt 1:1- und 1:0..1-Beziehungen. Letzteres ist das, wonach Sie suchen ("Eins zu Null oder Eins").

Ihre Versuche, fließend zu sprechen, besagen an beiden Enden erforderlich in einem Fall und optional an beiden Enden in dem anderen.

Was Sie brauchen, ist optional an einem Ende und required am anderen Ende.

Hier ist ein Beispiel aus dem Programmiercode E.F. Code First 

modelBuilder.Entity<PersonPhoto>()
.HasRequired(p => p.PhotoOf)
.WithOptional(p => p.Photo);

Die PersonPhoto-Entität verfügt über eine Navigationseigenschaft namens PhotoOf, die auf einen Person-Typ verweist. Der Person-Typ hat eine Navigationseigenschaft namens Photo, die auf den PersonPhoto-Typ verweist.

In den beiden verwandten Klassen verwenden Sie den Primärschlüssel jedes Typs, nicht Fremdschlüssel. Sie verwenden also nicht die Eigenschaften LoyaltyUserDetailId oder PIIUserId. Stattdessen hängt die Beziehung von den Id-Feldern beider Typen ab. 

Wenn Sie wie oben beschrieben die fließende API verwenden, müssen Sie nicht LoyaltyUser.Id als Fremdschlüssel angeben. EF ermittelt dies. 

Also, ohne dass Sie Ihren Code haben, um mich selbst zu testen (ich hasse es, dies aus meinem Kopf heraus zu tun) ... würde ich das in Ihren Code übersetzen

public class PIIUser
{
    public int Id { get; set; }    
    public LoyaltyUserDetail LoyaltyUserDetail { get; set; }
}

public class LoyaltyUserDetail
{
    public int Id { get; set; }
    public double? AvailablePoints { get; set; }    
    public PIIUser PIIUser { get; set; }
}

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
  modelBuilder.Entity<LoyaltyUserDetail>()
  .HasRequired(lu => lu.PIIUser )
  .WithOptional(pi => pi.LoyaltyUserDetail );
}

Die Eigenschaft LoyaltyUserDetails PIIUser ist required und die LoyaltyUserDetail-Eigenschaft von PIIUser ist optional.

Sie könnten am anderen Ende beginnen:

modelBuilder.Entity<PIIUser>()
.HasOptional(pi => pi.LoyaltyUserDetail)
.WithRequired(lu => lu.PIIUser);

die LoyaltyUserDetail-Eigenschaft von PIIUser ist jetzt optional und die PIIUser-Eigenschaft von LoyaltyUser ist erforderlich.

Sie müssen immer das Muster HAS/WITH verwenden.

HTH und FWIW, Eins-zu-Eins-Beziehungen (oder Eins-zu-Null-Eins-Beziehungen) sind eine der verwirrendsten Beziehungen, die zuerst im Code konfiguriert werden müssen, damit Sie nicht alleine sind! :)

91
Julie Lerman

Tun Sie es einfach so, wenn Sie eine 1: Viele-Beziehung zwischen LoyaltyUserDetail und PIIUser haben, so dass Ihre Zuordnung erfolgen sollte

modelBuilder.Entity<LoyaltyUserDetail>()
       .HasRequired(m => m.PIIUser )
       .WithMany()
       .HasForeignKey(c => c.LoyaltyUserDetailId);

EF sollte alle Fremdschlüssel erstellen, die Sie benötigen, und kümmert sich nicht um WithMany !

22

Es gibt mehrere Dinge, die mit Ihrem Code falsch sind. 

A 1: 1 Beziehung ist entweder: PK <-PK , wobei eine PK-Seite auch ein FK ist, oder PK <-FK + UC , wobei die FK-Seite ein Nicht-ist. PK und hat eine UC. Ihr Code zeigt, dass Sie FK <-FK haben, da Sie beide Seiten als FK definieren, aber das ist falsch. Ich erkenne, dass PIIUser die PK-Seite ist und LoyaltyUserDetail die FK-Seite ist. Dies bedeutet, dass PIIUser kein FK-Feld hat, LoyaltyUserDetail jedoch. 

Wenn die Beziehung " 1: 1 " optional ist, muss die FK-Seite mindestens ein Feld mit Nullwerten enthalten. 

p.s.w.g. Oben haben Sie Ihre Frage beantwortet, aber einen Fehler gemacht, dass er/sie auch eine FK in PIIUser definiert hat, was natürlich falsch ist, wie ich oben beschrieben habe. Definieren Sie das nullfähige FK-Feld in LoyaltyUserDetail, definieren Sie das Attribut in LoyaltyUserDetail, um es als FK-Feld zu markieren, geben Sie jedoch kein FK-Feld in PIIUser an.

Sie erhalten die Ausnahme, die Sie oben beschrieben haben, da keine Seite die PK-Seite ist (prinzipielles Ende). 

EF ist bei 1: 1 nicht besonders gut, da es nicht in der Lage ist, eindeutige Einschränkungen zu verarbeiten. Ich bin kein Experte für Code, daher weiß ich nicht, ob er eine UC erstellen kann oder nicht. 

(edit) Übrigens: A 1: 1 B (FK) bedeutet, dass nur 1 FK-Einschränkung erstellt wurde, wobei das Ziel von B auf die PK von A verweist, nicht auf 2.

3
Frans Bouma
public class User
{
    public int Id { get; set; }
    public int? LoyaltyUserId { get; set; }
    public virtual LoyaltyUser LoyaltyUser { get; set; }
}

public class LoyaltyUser
{
    public int Id { get; set; }
    public virtual User MainUser { get; set; }
}

        modelBuilder.Entity<User>()
            .HasOptional(x => x.LoyaltyUser)
            .WithOptionalDependent(c => c.MainUser)
            .WillCascadeOnDelete(false);

dies löst das Problem aufREFERENCEund FOREIGN KEYS  

wennAKTUALISIERUNGoderL&OUML;SCHENeines Datensatzes

2
pampi

Fügen Sie das ForeignKey-Attribut zur LoyaltyUserDetail-Eigenschaft hinzu:

public class PIIUser
{
    ...
    public int? LoyaltyUserDetailId { get; set; }
    [ForeignKey("LoyaltyUserDetailId")]
    public LoyaltyUserDetail LoyaltyUserDetail { get; set; }
    ...
}

Und die PIIUser-Eigenschaft:

public class LoyaltyUserDetail
{
    ...
    public int PIIUserId { get; set; }
    [ForeignKey("PIIUserId")]
    public PIIUser PIIUser { get; set; }
    ...
}
2
p.s.w.g

Mit den oben genannten Lösungen ist das eine verwirrend, dass der Primärschlüssel in beiden Tabellen als " Id" definiert ist. Wenn Sie einen Primärschlüssel haben, der auf dem Tabellennamen basiert, funktioniert er nicht Veranschaulichen dasselbe, dh die optionale Tabelle sollte nicht ihren eigenen Primärschlüssel definieren, stattdessen sollte derselbe Schlüsselname aus der Haupttabelle verwendet werden.

public class PIIUser
{
    // For illustration purpose I have named the PK as PIIUserId instead of Id
    // public int Id { get; set; }
    public int PIIUserId { get; set; }

    public int? LoyaltyUserDetailId { get; set; }
    public LoyaltyUserDetail LoyaltyUserDetail { get; set; }
}

public class LoyaltyUserDetail
{
    // Note: You cannot define a new Primary key separately as it would create one to many relationship
    // public int LoyaltyUserDetailId { get; set; }

    // Instead you would reuse the PIIUserId from the primary table, and you can mark this as Primary Key as well as foreign key to PIIUser table
    public int PIIUserId { get; set; } 
    public double? AvailablePoints { get; set; }

    public int PIIUserId { get; set; }
    public PIIUser PIIUser { get; set; }
}

Und dann gefolgt von 

modelBuilder.Entity<PIIUser>()
.HasOptional(pi => pi.LoyaltyUserDetail)
.WithRequired(lu => lu.PIIUser);

Würde ich den Trick tun, kann die akzeptierte Lösung dies nicht klar erklären, und es warf mich ein paar Stunden weg, um die Ursache zu finden

0
skjagini