Partager via


API Fluent - Relations

Remarque

Cette page fournit des informations sur la configuration des relations dans votre modèle Code First à l’aide de l’API Fluent. Pour obtenir des informations générales sur les relations dans E et sur la manière d’accéder aux données et de les manipuler à l’aide de relations, consultez Relations et propriétés de navigation.

Lorsque vous utilisez Code First, vous définissez votre modèle en définissant vos classes CLR de domaine. Par défaut, Entity Framework utilise les conventions Code First pour mapper vos classes au schéma de base de données. Si vous utilisez les conventions d’affectation de noms Code First, dans la plupart des cas, vous pouvez vous appuyer sur Code First pour configurer des relations entre vos tables en fonction des clés étrangères et des propriétés de navigation que vous définissez sur les classes. Si vous ne suivez pas les conventions lorsque vous définissez vos classes ou si vous souhaitez modifier la façon dont les conventions fonctionnent, vous pouvez utiliser l’API Fluent ou les annotations de données pour configurer vos classes pour que Code First puisse mapper les relations entre vos tables.

Présentation

Lors de la configuration d’une relation avec l’API Fluent, vous commencez par l’instance EntityTypeConfiguration, puis vous utilisez la méthode HasRequired, HasOptional ou HasMany pour spécifier le type de relation auquel cette entité participe. Les méthodes HasRequired et HasOptional prennent une expression lambda qui représente une propriété de navigation de référence. La méthode HasMany prend une expression lambda qui représente une propriété de navigation de collection. Vous pouvez ensuite configurer une propriété de navigation inverse à l’aide des méthodes WithRequired, WithOptional et WithMany. Ces méthodes ont des surcharges qui ne prennent pas d’arguments et peuvent être utilisées pour spécifier la cardinalité avec des navigations unidirectionnelles.

Vous pouvez ensuite configurer des propriétés de clé étrangère à l’aide de la méthode HasForeignKey. Cette méthode prend une expression lambda qui représente la propriété à utiliser comme clé étrangère.

Configuration d’une relation obligatoire à facultative (un-à–zéro-ou-un)

L’exemple suivant configure une relation un-à-zéro-ou-un. OfficeAssignment a la propriété InstructorID qui est une clé primaire et une clé étrangère, car le nom de la propriété ne suit pas la convention. La méthode HasKey est utilisée pour configurer la clé primaire.

// Configure the primary key for the OfficeAssignment
modelBuilder.Entity<OfficeAssignment>()
    .HasKey(t => t.InstructorID);

// Map one-to-zero or one relationship
modelBuilder.Entity<OfficeAssignment>()
    .HasRequired(t => t.Instructor)
    .WithOptional(t => t.OfficeAssignment);

Configuration d’une relation où les deux terminaisons sont obligatoires (un-à-un)

Dans la plupart des cas, Entity Framework peut déduire le type dépendant et le type principal dans une relation. Toutefois, lorsque les deux terminaisons de la relation sont obligatoires ou que les deux côtés sont facultatifs, Entity Framework ne peut pas identifier le type principal et le type dépendant. Lorsque les deux terminaisons de la relation sont obligatoires, utilisez WithRequiredPrincipal ou WithRequiredDependent après la méthode HasRequired. Lorsque les deux terminaisons de la relation sont facultatives, utilisez WithOptionalPrincipal ou WithOptionalDependent après la méthode HasOptional.

// Configure the primary key for the OfficeAssignment
modelBuilder.Entity<OfficeAssignment>()
    .HasKey(t => t.InstructorID);

modelBuilder.Entity<Instructor>()
    .HasRequired(t => t.OfficeAssignment)
    .WithRequiredPrincipal(t => t.Instructor);

Configuration d’une relation plusieurs-à-plusieurs

Le code suivant configure une relation plusieurs-à-plusieurs entre les types Cours et Instructor. Dans l’exemple suivant, les conventions Code First par défaut sont utilisées pour créer une table de jointure. Par conséquent, la table CourseInstructor est créée avec des colonnes Course_CourseID et Instructor_InstructorID.

modelBuilder.Entity<Course>()
    .HasMany(t => t.Instructors)
    .WithMany(t => t.Courses)

Si vous souhaitez spécifier le nom de la table de jointure et les noms des colonnes de la table, vous devez effectuer une configuration supplémentaire à l’aide de la méthode Map. Le code suivant génère la table CourseInstructor avec les colonnes CourseID et InstructorID.

modelBuilder.Entity<Course>()
    .HasMany(t => t.Instructors)
    .WithMany(t => t.Courses)
    .Map(m =>
    {
        m.ToTable("CourseInstructor");
        m.MapLeftKey("CourseID");
        m.MapRightKey("InstructorID");
    });

Configuration d’une relation avec une propriété de navigation

Une relation unidirectionnelle existe lorsqu’une propriété de navigation est définie sur une seule terminaison de la relation, et non sur les deux. Par convention, Code First interprète toujours une relation unidirectionnelle comme une relation une-à-plusieurs. Par exemple, si vous souhaitez une relation un-à-un entre Instructor et OfficeAssignment, où vous disposez d’une propriété de navigation uniquement sur le type Instructor, vous devez utiliser l’API Fluent pour configurer cette relation.

// Configure the primary Key for the OfficeAssignment
modelBuilder.Entity<OfficeAssignment>()
    .HasKey(t => t.InstructorID);

modelBuilder.Entity<Instructor>()
    .HasRequired(t => t.OfficeAssignment)
    .WithRequiredPrincipal();

Activation de la suppression en cascade

Vous pouvez configurer la suppression en cascade sur une relation à l’aide de la méthode WillCascadeOnDelete. Si une clé étrangère sur l’entité dépendante ne peut pas accepter la valeur Null, Code First définit la suppression en cascade sur la relation. Si une clé étrangère sur l’entité dépendante ne peut pas accepter la valeur Null, Code First ne définit pas de suppression en cascade sur la relation, et lorsque le principal est supprimé, la clé étrangère est définie sur Null.

Vous pouvez supprimer ces conventions de suppression en cascade à l’aide des éléments suivants :

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>()
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>()

Le code suivant configure la relation qui doit être obligatoire, puis désactive la suppression en cascade.

modelBuilder.Entity<Course>()
    .HasRequired(t => t.Department)
    .WithMany(t => t.Courses)
    .HasForeignKey(d => d.DepartmentID)
    .WillCascadeOnDelete(false);

Configuration d’une clé étrangère composite

Si la clé primaire sur le type Department se compose des propriétés DepartmentID et Name, vous devez configurer la clé primaire pour Department et la clé étrangère sur les types de cours comme suit :

// Composite primary key
modelBuilder.Entity<Department>()
.HasKey(d => new { d.DepartmentID, d.Name });

// Composite foreign key
modelBuilder.Entity<Course>()  
    .HasRequired(c => c.Department)  
    .WithMany(d => d.Courses)
    .HasForeignKey(d => new { d.DepartmentID, d.DepartmentName });

Renommage d’une clé étrangère qui n’est pas définie dans le modèle

Si vous choisissez de ne pas définir de clé étrangère sur le type CLR, mais que vous souhaitez spécifier le nom qu’il doit avoir dans la base de données, procédez comme suit :

modelBuilder.Entity<Course>()
    .HasRequired(c => c.Department)
    .WithMany(t => t.Courses)
    .Map(m => m.MapKey("ChangedDepartmentID"));

Configuration d’un nom de clé étrangère qui ne suit pas la convention Code First

Si la propriété de clé étrangère de la classe Course a été appelée SomeDepartmentID au lieu de DepartmentID, vous devez effectuer les opérations suivantes pour spécifier que someDepartmentID doit être la clé étrangère :

modelBuilder.Entity<Course>()
         .HasRequired(c => c.Department)
         .WithMany(d => d.Courses)
         .HasForeignKey(c => c.SomeDepartmentID);

Modèle utilisé dans des exemples

Le modèle Code First suivant est utilisé pour les exemples de cette page.

using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
// add a reference to System.ComponentModel.DataAnnotations DLL
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System;

public class SchoolEntities : DbContext
{
    public DbSet<Course> Courses { get; set; }
    public DbSet<Department> Departments { get; set; }
    public DbSet<Instructor> Instructors { get; set; }
    public DbSet<OfficeAssignment> OfficeAssignments { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // Configure Code First to ignore PluralizingTableName convention
        // If you keep this convention then the generated tables will have pluralized names.
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
    }
}

public class Department
{
    public Department()
    {
        this.Courses = new HashSet<Course>();
    }
    // Primary key
    public int DepartmentID { get; set; }
    public string Name { get; set; }
    public decimal Budget { get; set; }
    public System.DateTime StartDate { get; set; }
    public int? Administrator { get; set; }

    // Navigation property
    public virtual ICollection<Course> Courses { get; private set; }
}

public class Course
{
    public Course()
    {
        this.Instructors = new HashSet<Instructor>();
    }
    // Primary key
    public int CourseID { get; set; }

    public string Title { get; set; }
    public int Credits { get; set; }

    // Foreign key
    public int DepartmentID { get; set; }

    // Navigation properties
    public virtual Department Department { get; set; }
    public virtual ICollection<Instructor> Instructors { get; private set; }
}

public partial class OnlineCourse : Course
{
    public string URL { get; set; }
}

public partial class OnsiteCourse : Course
{
    public OnsiteCourse()
    {
        Details = new Details();
    }

    public Details Details { get; set; }
}

public class Details
{
    public System.DateTime Time { get; set; }
    public string Location { get; set; }
    public string Days { get; set; }
}

public class Instructor
{
    public Instructor()
    {
        this.Courses = new List<Course>();
    }

    // Primary key
    public int InstructorID { get; set; }
    public string LastName { get; set; }
    public string FirstName { get; set; }
    public System.DateTime HireDate { get; set; }

    // Navigation properties
    public virtual ICollection<Course> Courses { get; private set; }
}

public class OfficeAssignment
{
    // Specifying InstructorID as a primary
    [Key()]
    public Int32 InstructorID { get; set; }

    public string Location { get; set; }

    // When Entity Framework sees Timestamp attribute
    // it configures ConcurrencyCheck and DatabaseGeneratedPattern=Computed.
    [Timestamp]
    public Byte[] Timestamp { get; set; }

    // Navigation property
    public virtual Instructor Instructor { get; set; }
}