web-dev-qa-db-fra.com

Définition de plusieurs clés étrangères pour la même table dans Entity Framework Code First

J'ai deux entités dans mon application MVC et j'ai rempli la base de données avec l'approche Entity Framework 6 Code First. Il y a deux identifiants de ville dans l'entité Etudiant; l'un pour BirthCity, l'autre pour WorkingCity. Lorsque je définis les clés étrangères comme ci-dessus, une colonne supplémentaire est créée nommée City_ID dans la table Student après la migration. Y a-t-il une erreur ou comment définir ces FK? Merci d'avance.

Etudiant:

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; }
}


Ville:

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

    public string CityName { get; set; }


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

Pour obtenir ce que vous voulez, vous devez fournir une configuration supplémentaire.La convention Code First peut identifier les relations bidirectionnelles, mais pas lorsqu'il existe plusieurs relations bidirectionnelles entre deux entités.Vous pouvez ajouter une configuration (en utilisant Annotations de données ou API Fluent ) pour présenter ces informations au générateur de modèle. Avec les annotations de données, vous utiliserez une annotation appelée InverseProperty . Avec l'API Fluent, vous utiliserez une combinaison des méthodes Has/With pour spécifier les extrémités correctes de ces relations.

Utiliser les annotations de données pourrait ressembler à ceci:

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; }
}

De cette façon, vous spécifiez explicitement que vous souhaitez associer la propriété de navigation BirthCity avec la propriété de navigation Students à l'autre extrémité de la relation.

Utiliser Fluent Api pourrait ressembler à ceci:

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);
}

Avec cette dernière solution, vous n'avez pas besoin d'utiliser d'attribut.

Maintenant, la suggestion de @ChristPratt dans avoir une collection de Student dans votre classe City pour chaque relation est vraiment utile. Si vous faites cela, les configurations utilisant les annotations de données pourraient être de cette façon:

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; }
}

Ou en utilisant Fluent Api suivant la même idée:

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

Sheesh. Ça a été une longue journée. Il y a en fait un très gros problème flagrant avec votre code, en fait, que j'ai complètement raté quand j'ai commenté.

Le problème est que vous utilisez une seule collection d'étudiants sur City. Ce qui se passe réellement ici, c'est que EF ne peut pas décider à quelle clé étrangère il doit réellement mapper cette collection, donc il crée une autre clé étrangère spécifiquement pour suivre cette relation. Ensuite, en fait, vous n'avez pas de propriétés de navigation pour les collections d'étudiants dérivées de BirthCity et LivingCity.

Pour cela, vous devez passer à une configuration fluide, car il n'y a aucun moyen de configurer cela correctement en utilisant uniquement des annotations de données. Vous aurez également besoin d'une collection supplémentaire d'étudiants afin de pouvoir suivre les deux relations:

public class City
{
    ...

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

Ensuite, pour 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);
        }
    }
}

Et enfin dans votre contexte:

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