Trabajar con claves externas (Entity Framework)

A partir de la versión 4 de .NET Framework, puede exponer las propiedades de clave externa sobre tipos de entidad y definir relaciones a través de claves externas. La opción Incluir columnas de clave externa en el modelo del Asistente de Entity Data Model está seleccionada de forma predeterminada. Cuando esta opción está seleccionada, los objetos entidad generados tienen propiedades escalares que se asignan a columnas de clave externa.

Las propiedades de clave externa permiten crear o modificar una relación sin consultar el objeto principal. Cuando se incluyen propiedades de clave externa, aún es posible modificar una relación actualizando la referencia al objeto principal sobre el objeto dependiente, o agregando un objeto dependiente a una colección sobre el objeto principal. Una manera habitual de obtener acceso a referencias y colecciones sobre entidades es a través de propiedades de navegación. Para obtener más información, vea Propiedades de navegación.

Entity Framework intenta mantener las referencias, colecciones y propiedades de clave externa sincronizadas. Si actualiza una propiedad de clave externa de un objeto dependiente, se debería sincronizar la referencia a este objeto y la colección del objeto principal. Para que se produzca esta sincronización, las entidades deben asociarse al contexto del objeto y recibir seguimiento de Entity Framework . Para obtener más información, vea Resolución de identidades, administración de estados y seguimiento de cambios (Entity Framework) y Definir y administrar relaciones (Entity Framework).

Los ejemplos de este tema se basan en el Modelo School. Los tipos de entidad de estos ejemplos se generan mediante las herramientas de Entity Data Model . Los tipos generados reciben seguimiento por parte de Entity Framework. Si está trabajando con tipos POCO, vea Realizar el seguimiento de los cambios en entidades POCO (Entity Framework).

En el siguiente ejemplo se muestra cómo cambiar la relación entre dos objetos existentes.

Dim studentId As Integer = 6
Dim enrollmentID As Integer = 2

Using context = New SchoolEntities()
    ' Get StudentGrade. 
    Dim grade = (From g In context.StudentGrades
                 Where g.EnrollmentID = enrollmentID
                 Select g).First()

    ' Change the relationship. 
    grade.StudentID = studentId
    ' You can access Person reference object on the grade object 
    ' without loading the reference explicitly when 
    ' the lazy loading option is set to true. 
    Console.WriteLine(grade.Person.PersonID)
    ' Save the changes. 
    context.SaveChanges()
End Using
int studentId = 6;
int enrollmentID = 2;

using (var context = new SchoolEntities())
{
    // Get StudentGrade.
    var grade = (from g in context.StudentGrades
                 where g.EnrollmentID == enrollmentID
                 select g).First();

    // Change the relationship.
    grade.StudentID = studentId;
    // You can access Person reference object on the grade object
    // without loading the reference explicitly when
    // the lazy loading option is set to true.
    Console.WriteLine(grade.Person.PersonID);
    // Save the changes.
    context.SaveChanges();
}

En el siguiente ejemplo se muestra cómo establecer la relación entre el nuevo objeto dependiente y los objetos principales existentes estableciendo las propiedades de clave externa.

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

En el siguiente ejemplo se muestra cómo establecer la relación entre el nuevo objeto dependiente y el nuevo objeto principal.

Using context = New SchoolEntities()
    ' The database will generate PersonID. 
    ' The object context will get the ID 
    ' After the SaveChanges is called. 
    Dim newStudent = New Person With
    {
        .PersonID = 0,
        .LastName = "Li",
        .FirstName = "Yan"
     }
    ' The database will generate EnrollmentID. 
    ' The object context will get the ID 
    ' After the SaveChanges is called. 
    Dim newStudentGrade = New StudentGrade With
    {
        .EnrollmentID = 0,
        .Grade = CDec(4.0),
        .StudentID = 50
    }

    ' Add newStudent to object context. 
    ' The newStudent's state will change from Detached to Added. 
    context.People.AddObject(newStudent)

    ' To associate the new objects you can do one of the following: 
    ' Add the new dependent object to the principal object: newStudent.StudentGrades.Add(newStudentGrade). 
    ' Assign the reference (principal) object to the navigation property of the 
    ' dependent object: newStudentGrade.Person = newStudent. 
    ' Both of these methods will synchronize the navigation properties on both ends of the relationship. 

    ' Adding the newStudentGrade to newStudent will change newStudentGrade's status 
    ' from Detached to Added. 
    newStudent.StudentGrades.Add(newStudentGrade)
    ' Navigation properties in both directions will work immediately. 
    Console.WriteLine("Access StudentGrades navigation property to get the count: ", _
                      newStudent.StudentGrades.Count)
    Console.WriteLine("Access Person navigation property: {0} ", _
                      newStudentGrade.Person.FirstName)

    context.SaveChanges()
End Using
using (var context = new SchoolEntities())
{
    Person newStudent = new Person
    {
        // The database will generate PersonID.
        // The object context will get the ID 
        // After the SaveChanges is called.
        PersonID = 0,
        LastName = "Li",
        FirstName = "Yan"
    };
    StudentGrade newStudentGrade = new StudentGrade
    {
        // The database will generate EnrollmentID.
        // The object context will get the ID 
        // After the SaveChanges is called.
        EnrollmentID = 0,
        Grade = 4.0M,
        StudentID = 50
    };

    // Add newStudent to object context. 
    // The newStudent's state will change from Detached to Added.
    context.People.AddObject(newStudent);

    // To associate the new objects you can do one of the following:
    // Add the new dependent object to the principal object: newStudent.StudentGrades.Add(newStudentGrade).
    // Assign the reference (principal) object to the navigation property of the 
    // dependent object: newStudentGrade.Person = newStudent.
    // Both of these methods will synchronize the navigation properties on both ends of the relationship.

    // Adding the newStudentGrade to newStudent will change newStudentGrade's status
    // from Detached to Added.
    newStudent.StudentGrades.Add(newStudentGrade);
    // Navigation properties in both directions will work immediately.
    Console.WriteLine("Access StudentGrades navigation property to get the count: ",
        newStudent.StudentGrades.Count);
    Console.WriteLine("Access Person navigation property: {0} ", newStudentGrade.Person.FirstName);

    context.SaveChanges();
}

Con la asociación de claves externas, aún puede utilizar la referencia para establecer la relación como lo haría en la versión anterior a Entity Framework 4.

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

El siguiente ejemplo muestra una comunicación simple entre un cliente y un servicio, donde el cliente solicita un objeto del servicio, actualiza el objeto y llama al servicio para guardar los cambios en la base de datos.

El servicio define dos métodos:

Private Shared Function GetOriginalValue(ByVal ID As Integer) As StudentGrade
    Dim originalItem As StudentGrade
    Using context As New SchoolEntities()
        originalItem = context.StudentGrades.Where(Function(g) g.EnrollmentID = ID).FirstOrDefault()

        context.Detach(originalItem)
    End Using
    Return originalItem
End Function

Private Shared Sub SaveUpdates(ByVal updatedItem As StudentGrade)
    Using context As New SchoolEntities()
        ' Query for the StudentGrade object with the specified ID. 
        Dim original = (From o In context.StudentGrades
            Where o.EnrollmentID = updatedItem.EnrollmentID
            Select o).First()

        ' Apply changes. 
        context.StudentGrades.ApplyCurrentValues(updatedItem)

        ' Save changes. 
        context.SaveChanges()
    End Using
End Sub
static private StudentGrade GetOriginalValue(int ID)
{
    StudentGrade originalItem;
    using (SchoolEntities context
        = new SchoolEntities())
    {
        originalItem =
            context.StudentGrades.Where(g => g.EnrollmentID == ID).FirstOrDefault();

        context.Detach(originalItem);
    }
    return originalItem;
}

static private void SaveUpdates(StudentGrade updatedItem)
{
    using (SchoolEntities context
        = new SchoolEntities())
    {
        // Query for the StudentGrade object with the specified ID.
        var original = (from o in context.StudentGrades
                         where o.EnrollmentID == updatedItem.EnrollmentID
                         select o).First();

        // Apply changes.
        context.StudentGrades.ApplyCurrentValues(updatedItem);

        // Save changes.
        context.SaveChanges();
    }
}

El cliente actualiza los valores de las propiedades de clave externa y envía el objeto actualizado al servicio:

' A client calls a service to get the original object. 
Dim studentGrade As StudentGrade = GetOriginalValue(3)
' Change the relationships. 
studentGrade.CourseID = 5
studentGrade.StudentID = 10
' The client calls a method on a service to save the updates. 
// A client calls a service to get the original object.
StudentGrade studentGrade = GetOriginalValue(3);
// Change the relationships.
studentGrade.CourseID = 5;
studentGrade.StudentID = 10;
// The client calls a method on a service to save the updates. 
SaveUpdates(studentGrade);

Vea también

Conceptos

Definir y administrar relaciones (Entity Framework)
Trabajar con entidades POCO (Entity Framework)