web-dev-qa-db-de.com

Definieren Sie zuerst mehrere Fremdschlüssel für dieselbe Tabelle im Entity Framework-Code

Ich habe zwei Entitäten in meiner MVC-Anwendung und habe die Datenbank mit dem Entity Framework 6 Code First-Ansatz gefüllt. In der studentischen Entität gibt es zwei Stadt-IDs. einer für BirthCity, der andere für WorkingCity. Wenn ich die Fremdschlüssel wie oben definiere, wird nach der Migration eine zusätzliche Spalte mit dem Namen City_ID in der Student-Tabelle erstellt. Gibt es einen Fehler oder wie man diese FKs definiert? Danke im Voraus.

Student:

public class Student
{
    public int ID { get; set; }

    public string Name { get; set; }

    public string Surname { get; set; }

    public int BirthCityID { get; set; }

    public int LivingCityID { get; set; }


    [ForeignKey("BirthCityID")]
    public virtual City BirthCity { get; set; }

    [ForeignKey("LivingCityID")]
    public virtual City LivingCity { get; set; }
}


Stadt:

public class City
{
    public int ID { get; set; }

    public string CityName { get; set; }


    public virtual ICollection<Student> Students { get; set; }
}
36
Jack

Um das zu erreichen, was Sie möchten, müssen Sie eine zusätzliche Konfiguration bereitstellen.Code First Convention kann bidirektionale Beziehungen identifizieren, jedoch nicht, wenn es mehrere bidirektionale Beziehungen zwischen zwei Entitäten gibt. Sie können Konfiguration hinzufügen (mithilfe von Datenanmerkungen oder Fluent API ), um diese Informationen dem Modellbauer zu präsentieren. Bei Datenanmerkungen verwenden Sie eine Anmerkung mit dem Namen InverseProperty . Mit der Fluent-API verwenden Sie eine Kombination der Has/With -Methoden, um die richtigen Enden dieser Beziehungen anzugeben.

Die Verwendung von Datenanmerkungen könnte folgendermaßen aussehen:

public class Student
{
  public int ID { get; set; }

  public string Name { get; set; }

  public string Surname { get; set; }

  public int BirthCityID { get; set; }

  public int LivingCityID { get; set; }


  [ForeignKey("BirthCityID")]
  [InverseProperty("Students")]
  public virtual City BirthCity { get; set; }

  [ForeignKey("LivingCityID")]
  public virtual City LivingCity { get; set; }
}

Auf diese Weise geben Sie explizit an, dass Sie die Navigationseigenschaft BirthCity mit der Navigationseigenschaft Students am anderen Ende der Beziehung verknüpfen möchten.

Die Verwendung von Fluent Api könnte folgendermaßen aussehen:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
     modelBuilder.Entity<Student>().HasRequired(m => m.BirthCity)
                                 .WithMany(m => m.Students).HasForeignKey(m=>m.BirthCityId);
     modelBuilder.Entity<Student>().HasRequired(m => m.LivingCity)
                                 .WithMany().HasForeignKey(m=>m.LivingCityId);
}

Mit dieser letzten Lösung müssen Sie kein Attibute verwenden.

Nun, der Vorschlag von @ChristPratt, für jede Beziehung eine Sammlung von Student in Ihrer City -Klasse zu haben, ist wirklich nützlich. Wenn Sie dies tun, könnten die Konfigurationen mit Datenanmerkungen folgendermaßen aussehen:

public class Student
{
  public int ID { get; set; }

  public string Name { get; set; }

  public string Surname { get; set; }

  public int BirthCityID { get; set; }

  public int LivingCityID { get; set; }


  [ForeignKey("BirthCityID")]
  [InverseProperty("BirthCityStudents")]
  public virtual City BirthCity { get; set; }

  [ForeignKey("LivingCityID")]
  [InverseProperty("LivingCityStudents")]
  public virtual City LivingCity { get; set; }
}

Oder verwenden Sie Fluent Api nach der gleichen Idee:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
     modelBuilder.Entity<Student>().HasRequired(m => m.BirthCity)
               .WithMany(m => m.BirthCityStudents).HasForeignKey(m=>m.BirthCityId);
     modelBuilder.Entity<Student>().HasRequired(m => m.LivingCity)
               .WithMany(m => m.LivingCityStudents).HasForeignKey(m=>m.LivingCityId);
}
58
octavioccl

Meine Güte. Es war ein langer Tag. Es gibt tatsächlich ein sehr großes, eklatantes Problem mit Ihrem Code, das ich beim Kommentieren völlig übersehen habe.

Das Problem ist, dass Sie eine einzelne Sammlung von Schülern für City verwenden. Was hier tatsächlich passiert, ist, dass EF nicht entscheiden kann, welchem ​​Fremdschlüssel diese Auflistung tatsächlich zugeordnet werden soll. Daher wird ein weiterer Fremdschlüssel erstellt, um diese Beziehung zu verfolgen. Tatsächlich haben Sie dann keine Navigationseigenschaften für die Sammlungen von Schülern, die von BirthCity und LivingCity abgeleitet sind.

Zu diesem Zweck müssen Sie sich auf eine flüssige Konfiguration begeben, da dies nicht mit nur Datenanmerkungen richtig konfiguriert werden kann. Sie benötigen außerdem eine zusätzliche Sammlung von Schülern, damit Sie beide Beziehungen nachverfolgen können:

public class City
{
    ...

    public virtual ICollection<Student> BirthCityStudents { get; set; }
    public virtual ICollection<Student> LivingCityStudents { get; set; }
}

Dann für Student:

public class Student
{
    ...

    public class StudentMapping : EntityTypeConfiguration<Student>
    {
        public StudentMapping()
        {
            HasRequired(m => m.BirthCity).WithMany(m => m.BirthCityStudents);
            HasRequired(m => m.LivingCity).WithMany(m => m.LivingCityStudents);
        }
    }
}

Und zum Schluss in Ihrem Kontext:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Configurations.Add(new Student.StudentMapping());
}
18
Chris Pratt