Définition et gestion des relations (Entity Framework)

Dans Entity Framework , une entité peut être mise en rapport avec d'autres entités au moyen d'une association. Cette relation entre entités est définie dans le modèle conceptuel par l'élément Association. Chaque relation comporte deux terminaisons qui décrivent le type d'entité et la multiplicité du type (un, zéro ou un, ou plusieurs). La relation peut être régie par une contrainte référentielle qui décrit le rôle (principal ou dépendant) joué par chaque terminaison.

À partir de la version 4 du .NET Framework, vous pouvez inclure des clés étrangères dans le modèle conceptuel. L'option Inclure les colonnes clés étrangères dans le modèle de l'Assistant Entity Data Model est sélectionnée par défaut. Lorsque cette option est sélectionnée, les objets entité générés possèdent des propriétés scalaires qui se mappent aux colonnes clés étrangères. Lorsque les propriétés de clé étrangère sont incluses, vous pouvez créer ou modifier une relation en changeant la valeur de clé étrangère d'un objet dépendant. Ce type d'association s'appelle une association de clé étrangère.

Lorsque les colonnes clés étrangères ne sont pas incluses dans le modèle conceptuel, les informations d'association sont gérées en tant qu'objet indépendant. Les relations sont suivies par le biais de références d'objet plutôt que des propriétés de clé étrangère et sont représentées en tant qu'objet ObjectStateEntry dans l'objet ObjectStateManager. Ce type d'association s'appelle une association indépendante. La façon la plus courante de modifier une association indépendante est de modifier les propriétés de navigation générées pour chaque entité qui participe à l'association.

Dans les deux types d'association, chaque objet peut avoir une propriété de navigation pour chaque relation à laquelle il participe. Les propriétés de navigation permettent de parcourir et de gérer les relations dans les deux directions, en retournant soit un objet de référence, dans le cas d'une multiplicité un ou zéro ou un, soit une collection d'objets, dans le cas d'une multiplicité plusieurs.

Combinaison de relations

Vous pouvez choisir d'utiliser un type d'association dans votre modèle ou les deux à la fois. Toutefois, si vous incluez une table qui contient uniquement des clés étrangères (également appelée table de jointure pure) dans votre modèle, une association indépendante est utilisée pour gérer la relation de plusieurs à plusieurs pure, même si vous avez indiqué utiliser une association de clé étrangère dans votre modèle. L'Assistant Entity Data Model ne crée pas d'entité qui se mappe à une table de jointure pure.

Création et modification de relations

Dans Entity Framework , vous pouvez créer et modifier les relations de plusieurs façons.

  1. En affectant un nouvel objet à une propriété de navigation. Le code suivant crée une relation entre un objet order et l'objet customer. Si les objets sont attachés au contexte de l'objet, l'objet order est ajouté à la collection customer.Orders, et la propriété de clé étrangère correspondante de l'objet order prend la valeur de propriété de clé de l'objet customer :

    order.Customer = customer 
    
  2. En supprimant ou en ajoutant un objet dans une collection d'entités. Par exemple, vous pouvez utiliser la méthode Add pour ajouter un objet de type Order à la collection customer.Orders. Cette opération crée une relation entre un objet order particulier et un objet customer. Si les objets sont attachés au contexte de l'objet, la référence de l'objet customer et la propriété de clé étrangère de l'objet order correspondent à l'objet customer approprié :

    customer.Orders.Add(order)
    
  3. Dans les associations de clé étrangère, vous pouvez affecter une nouvelle valeur à une propriété de clé étrangère, comme dans l'exemple suivant. Si la référence est à l'état ajouté, la propriété de navigation de référence n'est pas synchronisée avec les valeurs de clé d'un nouvel objet tant que la méthode SaveChanges n'est pas appelée. La synchronisation n'a pas lieu, car le contexte de l'objet ne contient pas de clés permanentes pour les objets ajoutés tant qu'ils ne sont pas enregistrés. Pour plus d'informations, consultez Utilisation des clés d'entité (Entity Framework). Si les nouveaux objets doivent être entièrement synchronisés dès que vous définissez la relation, utilisez l'une des deux méthodes précédentes.

    order.CustomerID = newCustomer.CustomerID 
    order.CustomerID = null
    
  4. En créant la clé d'entité d'un objet spécifique. S'il existe déjà un objet avec cette clé dans le contexte de l'objet, la méthode CreateEntityKey retourne l'objet EntityKey de l'objet existant. Cette méthode est fournie pour assurer une compatibilité descendante avec le .NET Framework 3.5 SP1.

    order.CustomerReference.EntityKey = ctx.CreateEntityKey("EntitySetName", newObject)
    

Lorsque vous modifiez la relation des objets attachés au contexte de l'objet à l'aide de l'une des méthodes décrites ci-dessus, Entity Framework doit faire en sorte que les clés étrangères, les références et les collections restent synchronisées. Entity Framework gère cette synchronisation pour certains types d'objet éligibles :

Si vous utilisez des entités POCO sans proxys, vous devez appeler la méthode DetectChanges pour synchroniser les objets connexes dans le contexte de l'objet. Si vous utilisez des objets déconnectés, vous devez gérer la synchronisation manuellement.

Les exemples suivants indiquent comment utiliser la propriété de clé étrangère et la propriété de navigation pour associer les objets connexes. Avec les associations de clé étrangère, vous pouvez utiliser les deux méthodes pour créer ou modifier des relations. Avec les associations indépendantes, vous ne pouvez pas utiliser la propriété de clé étrangère.

' The following example creates a new StudentGrade object and associates 
' the StudentGrade with the Course and Person by 
' setting the foreign key properties. 

Using context As New SchoolEntities()
    ' The database will generate the EnrollmentID. 
    ' To create the association between the Course and StudentGrade, 
    ' and the Student and the StudentGrade, set the foreign key property 
    ' to the ID of the principal. 
    Dim newStudentGrade = New StudentGrade With
    {
        .EnrollmentID = 0,
        .Grade = CDec(4.0),
        .CourseID = 4022,
        .StudentID = 17
    }

    ' Adding the new object to the context will synchronize 
    ' the references with the foreign keys on the newStudentGrade object. 
    context.StudentGrades.AddObject(newStudentGrade)

    ' You can access Course and Student objects on the newStudentGrade object
    ' without loading the references explicitly because
    ' the lazy loading option is set to true in the constructor of SchoolEntities.
    Console.WriteLine("Student ID {0}:", newStudentGrade.Person.PersonID)
    Console.WriteLine("Course ID {0}:", newStudentGrade.Course.CourseID)

    context.SaveChanges()
End Using

' The following example creates a new StudentGrade and associates 
' the StudentGrade with the Course and Person by 
' setting the navigation properties to the Course and Person objects that were returned 
' by the query. 
' You do not need to call AddObject() in order to add the grade object 
' to the context, because when you assign the reference 
' to the navigation property the objects on both ends get synchronized by the Entity Framework. 
' Note, that the Entity Framework will not synchronize the ends untill the SaveChanges method 
' is called if your objects do not meet the change tracking requirements. 
Using context = New SchoolEntities()
    Dim courseID = 4022
    Dim course = (From c In context.Courses
                 Where c.CourseID = courseID
                 Select c).First()

    Dim personID = 17
    Dim student = (From p In context.People
                  Where p.PersonID = personID
                  Select p).First()

    ' The database will generate the EnrollmentID. 
    ' Use the navigation properties to create the association between the objects. 
    Dim newStudentGrade = New StudentGrade With
    {
        .EnrollmentID = 0,
        .Grade = CDec(4.0),
        .Course = course,
        .Person = student
    }
    context.SaveChanges()
End Using
// The following example creates a new StudentGrade object and associates
// the StudentGrade with the Course and Person by
// setting the foreign key properties. 

using (SchoolEntities context = new SchoolEntities())
{
    StudentGrade newStudentGrade = new StudentGrade
    {
        // The database will generate the EnrollmentID.
        EnrollmentID = 0,
        Grade = 4.0M,
        // To create the association between the Course and StudentGrade, 
        // and the Student and the StudentGrade, set the foreign key property 
        // to the ID of the principal.
        CourseID = 4022,
        StudentID = 17,
    };

    // Adding the new object to the context will synchronize
    // the references with the foreign keys on the newStudentGrade object.
    context.StudentGrades.AddObject(newStudentGrade);

    // You can access Course and Student objects on the newStudentGrade object
    // without loading the references explicitly because
    // the lazy loading option is set to true in the constructor of SchoolEntities.
    Console.WriteLine("Student ID {0}:", newStudentGrade.Person.PersonID);
    Console.WriteLine("Course ID {0}:", newStudentGrade.Course.CourseID);
    
    context.SaveChanges();
}

// The following example creates a new StudentGrade and associates
// the StudentGrade with the Course and Person by
// setting the navigation properties to the Course and Person objects that were returned
// by the query. 
// You do not need to call AddObject() in order to add the grade object
// to the context, because when you assign the reference 
// to the navigation property the objects on both ends get synchronized by the Entity Framework.
// Note, that the Entity Framework will not synchronize the ends untill the SaveChanges method
// is called if your objects do not meet the change tracking requirements. 
using (var context = new SchoolEntities())
{
    int courseID = 4022;
    var course = (from c in context.Courses
                 where c.CourseID == courseID
                 select c).First();

    int personID = 17;
    var student = (from p in context.People
                  where p.PersonID == personID
                  select p).First();

    StudentGrade grade = new StudentGrade
    {
        // The database will generate the EnrollmentID.
        Grade = 4.0M,
        // Use the navigation properties to create the association between the objects.
        Course = course,
        Person = student
    };
    context.SaveChanges();
}

Modification de l'état

Dans une association de clé étrangère, lorsque vous modifiez la relation, l'état Unchanged d'un objet dépendant passe à l'état Modified.

Dans une relation indépendante, la modification de la relation n'a pas pour effet de mettre à jour l'état de l'objet dépendant.

Gestion de l'accès concurrentiel

Dans les associations de clé étrangère comme dans les associations indépendantes, les contrôles d'accès concurrentiel sont basés sur les clés d'entité et d'autres propriétés d'entité qui sont définies dans la couche conceptuelle en affectant à l'attribut ConcurrencyMode la valeur fixed.

Toutefois, dans une association indépendante, le contrôle d'accès concurrentiel de relation est toujours pratiqué sur les objets situés aux deux extrémités d'une relation. Ce contrôle vérifie les valeurs de clé d'origine des terminaisons en question. Si vous modifiez la relation alors que l'objet est détaché du contexte de l'objet, vous devez recréer la relation d'origine (en redemandant ou en fournissant les valeurs de clé d'origine), attacher l'objet au contexte de l'objet, puis apporter les modifications appropriées à votre relation dans ce contexte d'objet.

Utilisation de clés qui se chevauchent

Des clés qui se chevauchent sont des clés composites qui ont certaines propriétés en commun dans l'entité qu'elles constituent. Une association indépendante ne peut pas contenir de clés qui se chevauchent. Pour modifier une association de clé étrangère qui comporte des clés qui se chevauchent, nous vous conseillons de modifier les valeurs de clé étrangère plutôt que d'utiliser les références d'objet.

Chargement d'objets connexes

Dans une association de clé étrangère, lorsque vous chargez une terminaison connexe d'un objet dépendant, l'objet connexe est chargé en fonction de la valeur de clé étrangère du dépendant actuellement en mémoire :

// Get the order where currently AddressID = 1.  SalesOrderHeader order = context.SalesOrderHeaders.First(o=>o.SalesOrderID == orderId);

// Use BillToAddressID foreign key property 
// to change the association.  order.BillToAddressID = 2
order.AddressReference.Load();  

Dans une association indépendante, la terminaison connexe d'un objet dépendant est interrogée en fonction de la valeur de clé étrangère actuellement présente dans la base de données. Toutefois, si la relation a été modifiée et que la propriété de référence de l'objet dépendant désigne un objet principal différent de celui qui est chargé dans le contexte de l'objet, Entity Framework essaie de créer une relation telle qu'elle est définie sur le client.

Considérations relatives aux relations d'identification et de non-identification

Lorsqu'une clé primaire de l'entité principale fait également partie de la clé primaire de l'entité dépendante, la relation est une relation d'identification. Dans une relation d'identification, l'entité dépendante ne peut pas exister sans l'entité principale. Cette contrainte provoque les comportements suivants dans une relation d'identification :

  • La suppression de l'objet principal supprime également l'objet dépendant. Du point de vue du comportement, cela revient à spécifier <OnDelete Action="Cascade" /> dans le modèle pour la relation.

  • La suppression de la relation supprime l'objet dépendant. L'appel de la méthode Remove sur l'objet EntityCollection marque la relation et l'objet dépendant pour suppression.

  • Lorsque vous créez un nouvel objet dépendant, l'objet principal doit exister dans le contexte de l'objet ou dans la source de données avant d'appeler SaveChanges. Si l'objet principal n'existe pas, une exception InvalidOperationException est levée.

Dans une relation de non-identification, si le modèle est basé sur des associations de clé étrangère, la suppression de l'objet principal entraîne l'affectation de la valeur Null aux clés étrangères des dépendants si celles-ci sont de type Nullable. Pour les objets dépendants qui ne peuvent pas exister sans objet principal, vous devez supprimer manuellement les objets dépendants ou leur affecter un nouvel objet principal. Vous pouvez également spécifier <OnDelete Action="Cascade" /> dans le modèle pour que les objets dépendants soient supprimés en même temps que l'objet principal connexe.

Dans cette section

Propriétés de navigation

Procédure : utiliser la propriété de clé étrangère pour modifier des relations entre des objets

Procédure : utiliser EntityReference pour modifier les relations entre des objets (Entity Framework)

Procédure : modifier les relations entre des entités POCO (Entity Framework)

Voir aussi

Tâches

Procédure : utiliser EntityReference pour modifier les relations entre des objets (Entity Framework)
Procédure : utiliser la propriété de clé étrangère pour modifier des relations entre des objets

Concepts

Création, ajout, modification et suppression d'objets (Entity Framework)
Utilisation d'entités POCO (Entity Framework)