Definir y administrar relaciones (Entity Framework)

En Entity Framework , una entidad se puede relacionar con otras entidades por medio de una asociación. Esta relación entre entidades se define en el modelo conceptual mediante el elemento Asociación. Cada relación contiene dos extremos que describen el tipo de entidad y la multiplicidad del tipo (uno, cero o uno, o varios). La relación puede venir gobernada por una restricción referencial, la cual describe qué extremo de la relación constituye un rol principal y cuál es un rol dependiente.

A partir de la versión 4 de .NET Framework, es posible incluir claves externas en el modelo conceptual. 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. Con las propiedades de clave externa incluidas, puede crear o cambiar una relación modificando el valor de clave externa sobre un objeto dependiente. Este tipo de asociación se denomina asociación de clave externa.

Cuando las columnas de clave externa no están incluidas en el modelo conceptual, la información de asociación se administra como un objeto independiente. El seguimiento de las relaciones se realiza a través de referencias a objetos, en lugar de propiedades de clave externa, y se representan como un objeto ObjectStateEntry en el ObjectStateManager. Este tipo de asociación se denomina asociación independiente. La manera más habitual de modificar una asociación independiente consiste en modificar las propiedades de navegación que se generan para cada entidad que participa en la asociación.

En ambos tipos de asociaciones, cada objeto puede tener una propiedad de navegación para cada relación en la que participa. Las propiedades de navegación permiten navegar y administrar las relaciones en ambas direcciones, devolviendo un objeto de referencia, si la multiplicidad es de uno, o cero o uno, o bien una colección de objetos, si la multiplicidad es de varios.

Mezclar relaciones

Puede decidir entre utilizar uno o ambos tipos de asociaciones en su modelo. Sin embargo, si incluye una tabla que solo contiene claves externas (denominada también tabla combinada pura) en su modelo, se utilizará una asociación independiente para administrar la relación pura de varios a varios aunque haya especificado el uso de una asociación de clave externa en su modelo. El Asistente de Entity Data Model no crea ninguna entidad asignada a una tabla combinada pura.

Crear y modificar relaciones

En Entity Framework , puede crear y modificar relaciones de varias maneras.

  1. Asignando un nuevo objeto a una propiedad de navegación. El siguiente código crea una relación entre un order y el customer. Si los objetos se adjuntan al contexto del objeto, el objeto order se agrega a la colección customer.Orders, y la propiedad de clave externa correspondiente en el objeto order queda establecida con el valor de propiedad de clave del cliente:

    order.Customer = customer 
    
  2. Eliminando o agregando un objeto en una colección de entidades. Por ejemplo, puede utilizar el método Add para agregar un objeto de tipo Order a la colección customer.Orders. Esta operación crea una relación entre un order determinado y un customer. Si los objetos se adjuntan al contexto del objeto, la referencia del cliente y la propiedad de clave externa en el objeto order quedarán establecidos con el customer adecuado:

    customer.Orders.Add(order)
    
  3. En asociaciones de clave externa, puede asignar un nuevo valor a una propiedad de clave externa, como en el siguiente ejemplo. Si la referencia se da en el estado agregado, la propiedad de navegación de referencia no estará sincronizada con los valores de clave de un nuevo objeto hasta que se llame a SaveChanges. La sincronización no se produce porque el contexto del objeto no contiene claves permanentes para objetos agregados hasta que se guardan. Para obtener más información, vea Trabajar con claves de entidad (Entity Framework). Si necesita que los nuevos objetos estén totalmente sincronizados tan pronto como se establece la relación, utilice uno de los dos métodos anteriores.

    order.CustomerID = newCustomer.CustomerID 
    order.CustomerID = null
    
  4. Creando la clave de entidad para un objeto específico. Si el objeto con esta clave ya existe en el contexto del objeto, el método CreateEntityKey devolverá el objeto EntityKey del objeto existente. Este método se incluye para proporcionar compatibilidad con .NET Framework 3.5 SP1.

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

Al cambiar la relación de los objetos adjuntos al contexto del objeto utilizando uno de los métodos descritos anteriormente, Entity Framework necesita mantener sincronizadas las claves externas, referencias y colecciones. Entity Framework administrará esta sincronización para ciertos tipos de objetos válidos:

Si está utilizando entidades POCO sin objetos proxy, deberá llamar al método DetectChanges para sincronizar los objetos relacionados en el contexto del objeto. Si está trabajando con objetos desconectados, deberá administrar la sincronización manualmente.

En los siguientes ejemplos se muestra cómo utilizar la propiedad de clave externa y la propiedad de navegación para asociar los objetos relacionados. Con las asociaciones de clave externa, puede utilizar ambos métodos para cambiar, crear o modificar relaciones. Con asociaciones independientes, no puede utilizar la propiedad 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 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();
}

Modificación de estados

En una asociación de clave externa, al cambiar la relación, el estado de un objeto dependiente con un estado Unchanged cambiará a Modified.

En una relación independiente, al cambiar la relación no se actualizará el estado del objeto dependiente.

Administrar la simultaneidad

Tanto en las asociaciones independientes como en las de clave externa, las comprobaciones de simultaneidad se basan en las claves de entidad y otras propiedades de entidad que se definen en el nivel conceptual estableciendo el atributo ConcurrencyMode en fixed.

En una asociación independiente, sin embargo, la comprobación de simultaneidad de una relación se realiza siempre sobre objetos de ambos extremos de una relación. Esta comprobación verifica los valores de clave originales de los extremos relacionados. Si modifica la relación mientras el objeto se encuentra desasociado del contexto del objeto, deberá volver a crear la relación original (volviendo a consultar o transmitiendo los valores de clave originales), adjuntar el objeto al contexto del objeto y realizar entonces los cambios apropiados a la relación en ese contexto del objeto.

Trabajar con claves superpuestas

Las claves superpuestas son claves compuestas en las que algunas propiedades de la clave también forman parte de otra clave de la entidad. No es posible tener una clave superpuesta en una asociación independiente. Para cambiar una asociación de clave externa que incluya claves superpuestas, recomendamos modificar los valores de clave externa en lugar de utilizar las referencias a objetos.

Cargar objetos relacionados

En una asociación de clave externa, al cargar un extremo relacionado de un objeto dependiente, el objeto relacionado se cargará dependiendo del valor de clave externa del objeto dependiente actualmente en memoria:

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

En una asociación independiente, se consulta el extremo relacionado de un objeto dependiente de acuerdo con el valor de clave externa actualmente en la base de datos. Sin embargo, si se modificó la relación, y la propiedad de referencia en el objeto dependiente señala a un objeto principal diferente cargado en el contexto del objeto, Entity Framework intentará crear una relación tal como se define en el cliente.

Consideraciones para relaciones de identificación y no identificación

Cuando una clave principal de la entidad principal también forma parte de la clave principal de la entidad dependiente, la relación es una relación de identificación. En una relación de identificación, la entidad dependiente no puede existir sin la entidad principal. Esta restricción produce los siguientes comportamientos en una relación de identificación:

  • Al eliminar el objeto principal, también se elimina el objeto dependiente. Este comportamiento es equivalente a especificar <OnDelete Action="Cascade" /> en el modelo para la relación.

  • Al quitar la relación se elimina el objeto dependiente. Al llamar al método Remove sobre el objeto EntityCollection, se marcan tanto la relación como el objeto dependiente para su eliminación.

  • Cuando se crea un nuevo objeto dependiente, el objeto principal debe existir en el contexto del objeto o en el origen de datos antes de llamar a SaveChanges. Si el objeto principal no existe, se producirá una excepción InvalidOperationException.

En una relación que no es de identificación, si el modelo está basado en asociaciones de clave externa, al eliminar el objeto principal se establecerán las claves externas de los objetos dependientes en null si admiten valores NULL. Para objetos dependientes que no pueden existir sin un objeto principal, deberá eliminar manualmente los objetos dependientes o asignar un nuevo objeto principal a los objetos dependientes. Como alternativa, también puede especificar <OnDelete Action="Cascade" /> en el modelo para asegurarse de que los objetos dependientes se eliminan al eliminar el objeto principal relacionado.

En esta sección

Propiedades de navegación

Cómo: Utilizar la propiedad de clave externa para cambiar las relaciones entre objetos

Cómo: Usar un objeto EntityReference para cambiar las relaciones entre los objetos (Entity Framework)

Cómo: Cambiar las relaciones entre entidades POCO (Entity Framework)

Vea también

Tareas

Cómo: Usar un objeto EntityReference para cambiar las relaciones entre los objetos (Entity Framework)
Cómo: Utilizar la propiedad de clave externa para cambiar las relaciones entre objetos

Conceptos

Crear, agregar, modificar y eliminar objetos (Entity Framework)
Trabajar con entidades POCO (Entity Framework)