Partilhar via


Defining and Managing Relationships

In the Entity Framework, an entity can be related to other entities through an association. This relationship between entities is defined in the conceptual model by the Association element. Each relationship contains two ends that describe the entity type and the multiplicity of the type (one, zero-or-one, or many). The relationship may be governed by a referential constraint, which describes which end in the relationship is a principal role and which is a dependent role.

Starting with the .NET Framework version 4, you can include foreign keys in the conceptual model. The Include foreign key columns in the model option in the Entity Data Model Wizard is selected by default. When this option is selected, the generated entity objects have scalar properties that map to foreign key columns. With foreign key properties included, you can create or change a relationship by modifying the foreign key value on a dependent object. This kind of association is called a foreign key association.

When foreign key columns are not included in the conceptual model, the association information is managed as an independent object. Relationships are tracked through object references instead of foreign key properties, and are represented as an ObjectStateEntry object in the ObjectStateManager.This type of association is called an independent association. The most common way to modify an independent association is to modify the navigation properties that are generated for each entity that participates in the association.

In both types of associations, every object can have a navigation property for every relationship in which it participates. The navigation properties allow you to navigate and manage relationships in both directions, returning either a reference object, if the multiplicity is either one or zero-or-one, or a collection of objects, if the multiplicity is many.

Mixing Relationships

You can choose to use one or both types of associations in your model. However, if you include a table that contains only foreign keys (also called a pure join table) in your model, an independent association will be used to manage the pure many-to-many relationship even if you specified to use a foreign key association in your model. The Entity Data Model Wizard does not create an entity that maps to a pure join table.

Creating and Modifying Relationships

In the Entity Framework, you can create and modify relationships in several ways.

  1. By assigning a new object to a navigation property. The following code creates a relationship between an order and the customer. If the objects are attached to the object context, the order is added to the customer.Orders collection, and the corresponding foreign key property on the order object is set to the key property value of the customer:

    order.Customer = customer 
    
  2. By deleting or adding an object in an entity collection. For example, you can use the Add method to add an object of type Order to the customer.Orders collection. This operation creates a relationship between a particular order and customer. If the objects are attached to the object context, the customer reference and the foreign key property on the order object will be set to the appropriate customer:

    customer.Orders.Add(order)
    
  3. In foreign key associations, you can assign a new value to a foreign key property, as in the following example.

    Note

    If the reference is in the added state, the reference navigation property will not be synchronized with the key values of a new object until SaveChanges is called. Synchronization does not occur because the object context does not contain permanent keys for added objects until they are saved. For more information, see Working with Entity Keys. If you must have new objects fully synchronized as soon as you set the relationship, use one of the previous two methods.

    order.CustomerID = newCustomer.CustomerID 
    order.CustomerID = null
    
  4. By creating the entity key for a specific object. If the object with this key already exists in the object context, the CreateEntityKey method will return the EntityKey of the existing object. This method is provided for backward compatibility with the .NET Framework 3.5 SP1.

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

When you change the relationship of the objects attached to the object context by using one of the methods described above, the Entity Framework needs to keep foreign keys, references, and collections in sync. The Entity Framework will manage this synchronization for certain types of eligible objects: 

If you are using POCO entities without proxies, you must call the DetectChanges method to synchronize the related objects in the object context. If you are working with disconnected objects you must manually manage the synchronization.

The following examples show how to use the foreign key property and the navigation property to associate the related objects. With foreign key associations, you can use both methods to change create or modify relationships. With independent associations, you cannot use the foreign key property.

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

State Modification

In a foreign key association, when you change the relationship, the state of a dependent object with an Unchanged state will change to Modified.

In an independent relationship, changing the relationship will not update the state of the dependent object.

Managing Concurrency

In both foreign key and independent associations, concurrency checks are based on the entity keys and other entity properties that are defined in the conceptual layer by setting the ConcurrencyMode attribute to fixed.

In an independent association, however, the relationship concurrency check is always performed on objects on both ends of a relationship. This check verifies the original key values of the related ends. If you modify the relationship while the object is detached from the object context, you must recreate the original relationship (either by re-querying or carrying the original key values), attach the object to the object context, and then make the appropriate changes to your relationship in that object context.

Working with Overlapping Keys

Overlapping keys are composite keys where some properties in the key are also part of another key in the entity. You cannot have an overlapping key in an independent association. To change a foreign key association that includes overlapping keys, we recommend that you modify the foreign key values instead of using the object references.

In a foreign key association, when you load a related end of a dependent object, the related object will be loaded based on the foreign key value of the dependent that is currently in memory:

// 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();

In an independent association, the related end of a dependent object is queried based on the foreign key value that is currently in the database. However, if the relationship was modified, and the reference property on the dependent object points to a different principal object that is loaded in the object context, the Entity Framework will try to create a relationship as it is defined on the client.

Considerations for Identifying and Non-identifying Relationships

When a primary key of the principal entity is also part of the primary key of the dependent entity, the relationship is an identifying relationship. In an identifying relationship the dependent entity cannot exist without the principal entity. This constraint causes the following behaviors in an identifying relationship:

  • Deleting the principal object also deletes the dependent object. This is the same behavior as specifying <OnDelete Action="Cascade" /> in the model for the relationship.

  • Removing the relationship deletes the dependent object. Calling the Remove method on the EntityCollection marks both the relationship and the dependent object for deletion.

  • When you create a new dependent object, the principal object must exist in the object context or in the data source before you call SaveChanges. If the principal object does not exist, an InvalidOperationException will be raised.

In a non-identifying relationship, if the model is based on foreign key associations, deleting the principal object will set foreign keys of dependents to null if they are nullable. For dependent objects that cannot exist without a principal, you must manually delete the dependent objects or assign a new principal object to the dependent objects. Alternatively, you can specify <OnDelete Action="Cascade" /> in the model to ensure that dependent objects are deleted when you delete the related principal object.

In This Section

Navigation Properties

How to: Use the Foreign Key Property to Change Relationships Between Objects

How to: Use an EntityReference to Change Relationships Between Objects

How to: Change Relationships Between POCO Entities

See Also

Tasks

How to: Use an EntityReference to Change Relationships Between Objects
How to: Use the Foreign Key Property to Change Relationships Between Objects

Concepts

Creating, Adding, Modifying, and Deleting Objects
Working with POCO Entities