API fluidas: relacionesFluent API - Relationships

Nota

En esta página se proporciona información sobre cómo configurar las relaciones en el modelo de Code First mediante la API fluida.This page provides information about setting up relationships in your Code First model using the fluent API. Para obtener información general sobre las relaciones en EF y cómo obtener acceso a los datos y manipularlos mediante relaciones, vea relaciones & propiedades de navegación.For general information about relationships in EF and how to access and manipulate data using relationships, see Relationships & Navigation Properties.

Cuando se trabaja con Code First, se define el modelo definiendo las clases CLR del dominio.When working with Code First, you define your model by defining your domain CLR classes. De forma predeterminada, Entity Framework utiliza las convenciones de Code First para asignar las clases al esquema de la base de datos.By default, Entity Framework uses the Code First conventions to map your classes to the database schema. Si utiliza las convenciones de nomenclatura de Code First, en la mayoría de los casos puede confiar en Code First para configurar las relaciones entre las tablas en función de las claves externas y las propiedades de navegación que defina en las clases.If you use the Code First naming conventions, in most cases you can rely on Code First to set up relationships between your tables based on the foreign keys and navigation properties that you define on the classes. Si no sigue las convenciones al definir las clases, o si desea cambiar la forma en que funcionan las convenciones, puede usar la API fluida o las anotaciones de datos para configurar las clases de modo que Code First pueda asignar las relaciones entre las tablas.If you do not follow the conventions when defining your classes, or if you want to change the way the conventions work, you can use the fluent API or data annotations to configure your classes so Code First can map the relationships between your tables.

IntroducciónIntroduction

Al configurar una relación con la API fluida, empiece con la instancia de EntityTypeConfiguration y, a continuación, use el método HasRequired, HasOptional o HasMany para especificar el tipo de relación en la que participa esta entidad.When configuring a relationship with the fluent API, you start with the EntityTypeConfiguration instance and then use the HasRequired, HasOptional, or HasMany method to specify the type of relationship this entity participates in. Los métodos HasRequired y HasOptional toman una expresión lambda que representa una propiedad de navegación de referencia.The HasRequired and HasOptional methods take a lambda expression that represents a reference navigation property. El método HasMany toma una expresión lambda que representa una propiedad de navegación de colección.The HasMany method takes a lambda expression that represents a collection navigation property. Después, puede configurar una propiedad de navegación inversa mediante los métodos WithRequired, WithOptional y WithMany.You can then configure an inverse navigation property by using the WithRequired, WithOptional, and WithMany methods. Estos métodos tienen sobrecargas que no toman argumentos y se pueden usar para especificar la cardinalidad con navegaciones unidireccionales.These methods have overloads that do not take arguments and can be used to specify cardinality with unidirectional navigations.

Después, puede configurar las propiedades de clave externa mediante el método HasForeignKey.You can then configure foreign key properties by using the HasForeignKey method. Este método toma una expresión lambda que representa la propiedad que se va a usar como clave externa.This method takes a lambda expression that represents the property to be used as the foreign key.

Configuración de una relación requerida-to-Optional (uno a cero o uno)Configuring a Required-to-Optional Relationship (One-to–Zero-or-One)

En el ejemplo siguiente se configura una relación de uno a cero o uno.The following example configures a one-to-zero-or-one relationship. OfficeAssignment tiene la propiedad InstructorID que es una clave principal y una clave externa, ya que el nombre de la propiedad no sigue la Convención que el método Haskey (utiliza para configurar la clave principal.The OfficeAssignment has the InstructorID property that is a primary key and a foreign key, because the name of the property does not follow the convention the HasKey method is used to configure the primary key.

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

Configurar una relación en la que se requieren ambos extremos (uno a uno)Configuring a Relationship Where Both Ends Are Required (One-to-One)

En la mayoría de los casos Entity Framework puede deducir qué tipo es el dependiente y cuál es la entidad de seguridad de una relación.In most cases Entity Framework can infer which type is the dependent and which is the principal in a relationship. Sin embargo, cuando se requieren ambos extremos de la relación o ambos lados son opcionales Entity Framework no puede identificar el dependiente y el principal.However, when both ends of the relationship are required or both sides are optional Entity Framework cannot identify the dependent and principal. Cuando se requieran ambos extremos de la relación, use WithRequiredPrincipal o WithRequiredDependent después del método HasRequired.When both ends of the relationship are required, use WithRequiredPrincipal or WithRequiredDependent after the HasRequired method. Cuando ambos extremos de la relación sean opcionales, use WithOptionalPrincipal o WithOptionalDependent después del método HasOptional.When both ends of the relationship are optional, use WithOptionalPrincipal or WithOptionalDependent after the HasOptional method.

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

Configuración de una relación de varios a variosConfiguring a Many-to-Many Relationship

El siguiente código configura una relación de varios a varios entre los tipos Course y instructor.The following code configures a many-to-many relationship between the Course and Instructor types. En el ejemplo siguiente, se usan las convenciones de Code First predeterminadas para crear una tabla de combinación.In the following example, the default Code First conventions are used to create a join table. Como resultado, la tabla CourseInstructor se crea con Course_CourseID y Instructor_InstructorID columnas.As a result the CourseInstructor table is created with Course_CourseID and Instructor_InstructorID columns.

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

Si desea especificar el nombre de la tabla de combinación y los nombres de las columnas de la tabla, debe realizar una configuración adicional mediante el método map.If you want to specify the join table name and the names of the columns in the table you need to do additional configuration by using the Map method. El código siguiente genera la tabla CourseInstructor con las columnas CourseID y InstructorID.The following code generates the CourseInstructor table with CourseID and InstructorID columns.

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

Configurar una relación con una propiedad de navegaciónConfiguring a Relationship with One Navigation Property

Una relación unidireccional (también denominada unidireccional) es cuando se define una propiedad de navegación solo en uno de los extremos de la relación y no en ambos.A one-directional (also called unidirectional) relationship is when a navigation property is defined on only one of the relationship ends and not on both. Por Convención, Code First siempre interpreta una relación unidireccional como uno a varios.By convention, Code First always interprets a unidirectional relationship as one-to-many. Por ejemplo, si desea una relación de uno a uno entre instructor y OfficeAssignment, donde tiene una propiedad de navegación solo en el tipo de instructor, debe usar la API fluida para configurar esta relación.For example, if you want a one-to-one relationship between Instructor and OfficeAssignment, where you have a navigation property on only the Instructor type, you need to use the fluent API to configure this relationship.

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

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

Habilitar la eliminación en cascadaEnabling Cascade Delete

Puede configurar la eliminación en cascada en una relación mediante el método WillCascadeOnDelete.You can configure cascade delete on a relationship by using the WillCascadeOnDelete method. Si una clave externa de la entidad dependiente no admite valores NULL, Code First establece Cascade delete en la relación.If a foreign key on the dependent entity is not nullable, then Code First sets cascade delete on the relationship. Si una clave externa de la entidad dependiente admite valores NULL, Code First no establece Cascade delete en la relación y, cuando se elimina la entidad de seguridad, la clave externa se establecerá en NULL.If a foreign key on the dependent entity is nullable, Code First does not set cascade delete on the relationship, and when the principal is deleted the foreign key will be set to null.

Puede quitar estas convenciones de eliminación en cascada mediante:You can remove these cascade delete conventions by using:

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

El código siguiente configura la relación para que sea necesaria y, a continuación, deshabilita la eliminación en cascada.The following code configures the relationship to be required and then disables cascade delete.

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

Configurar una clave externa compuestaConfiguring a Composite Foreign Key

Si la clave principal del tipo de departamento consta de las propiedades DepartmentID y Name, debe configurar la clave principal para el Departamento y la clave externa en los tipos de curso como se indica a continuación:If the primary key on the Department type consisted of DepartmentID and Name properties, you would configure the primary key for the Department and the foreign key on the Course types as follows:

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

Cambiar el nombre de una clave externa que no está definida en el modeloRenaming a Foreign Key That Is Not Defined in the Model

Si decide no definir una clave externa en el tipo CLR, pero desea especificar el nombre que debe tener en la base de datos, haga lo siguiente:If you choose not to define a foreign key on the CLR type, but want to specify what name it should have in the database, do the following:

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

Configuración de un nombre de clave externa que no sigue la Convención de Code FirstConfiguring a Foreign Key Name That Does Not Follow the Code First Convention

Si la propiedad de clave externa de la clase Course se llama SomeDepartmentID en lugar de DepartmentID, deberá hacer lo siguiente para especificar que SomeDepartmentID será la clave externa:If the foreign key property on the Course class was called SomeDepartmentID instead of DepartmentID, you would need to do the following to specify that you want SomeDepartmentID to be the foreign key:

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

Modelo usado en los ejemplosModel Used in Samples

El siguiente modelo de Code First se usa para los ejemplos de esta página.The following Code First model is used for the samples on this 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; }
}