Fluent API – relace

Poznámka:

Tato stránka obsahuje informace o nastavení relací v modelu Code First pomocí rozhraní FLUENT API. Obecné informace o relacích v EF a o tom, jak přistupovat k datům pomocí relací a manipulovat s nimi, naleznete v tématu Relace a vlastnosti navigace.

Při práci s Code First definujete model definováním tříd CLR domény. Ve výchozím nastavení Entity Framework používá konvence Code First k mapování tříd na schéma databáze. Pokud používáte konvence vytváření názvů Code First, můžete ve většině případů spoléhat na Code First, abyste nastavili relace mezi tabulkami na základě cizích klíčů a navigačních vlastností, které definujete pro třídy. Pokud při definování tříd nedodržíte konvence nebo pokud chcete změnit způsob fungování konvencí, můžete ke konfiguraci tříd použít fluentní rozhraní API nebo datové poznámky, aby Code First mohl namapovat relace mezi tabulkami.

Úvod

Když konfigurujete relaci s rozhraním FLUENT API, začnete s instancí EntityTypeConfiguration a pak použijete metodu HasRequired, HasOptional nebo HasMany k určení typu relace, do které se tato entita účastní. Metody HasRequired a HasOptional berou výraz lambda, který představuje referenční navigační vlastnost. Metoda HasMany přebírá výraz lambda, který představuje vlastnost navigace kolekce. Inverzní navigační vlastnost pak můžete nakonfigurovat pomocí metod WithRequired, WithOptional a WithMany. Tyto metody mají přetížení, které nepřebírají argumenty a lze je použít k určení kardinality s jednosměrnými navigacemi.

Vlastnosti cizího klíče pak můžete nakonfigurovat pomocí Metody HasForeignKey. Tato metoda přebírá výraz lambda, který představuje vlastnost, která se má použít jako cizí klíč.

Konfigurace relace povinné k volitelné (1:0 nebo 1)

Následující příklad konfiguruje relaci 1:0 nebo 1. OfficeAssignment má vlastnost InstructorID, která je primárním klíčem a cizím klíčem, protože název vlastnosti neodpovídá konvenci HasKey metoda se používá ke konfiguraci primárního klíče.

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

Konfigurace relace, kde jsou vyžadovány oba konce (1:1)

Ve většině případů Entity Framework může odvodit, který typ je závislý a který je objektem zabezpečení v relaci. Pokud jsou však oba konce relace povinné, nebo obě strany jsou volitelné Entity Framework nemohou identifikovat závislé a hlavní objekty. Pokud jsou oba konce relace požadovány, použijte WithRequiredPrincipal nebo WithRequiredDependent po HasRequired metoda. Pokud jsou oba konce relace volitelné, použijte WithOptionalPrincipal nebo WithOptionalDependent za HasOptional metoda.

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

Konfigurace relace M:N

Následující kód konfiguruje vztah M:N mezi typy kurzu a instruktora. V následujícím příkladu se výchozí konvence Code First používají k vytvoření tabulky spojení. Výsledkem je vytvoření tabulky CourseInstructor se sloupci Course_CourseID a Instructor_InstructorID.

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

Pokud chcete zadat název tabulky spojení a názvy sloupců v tabulce, musíte provést další konfiguraci pomocí metody Map. Následující kód vygeneruje tabulku CourseInstructor se sloupci CourseID a InstructorID.

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

Konfigurace relace pomocí jedné navigační vlastnosti

Jednosměrná relace (označovaná také jako jednosměrná) je, když je navigační vlastnost definována pouze na jednom konci relace, a ne na obou. Podle konvence Code First vždy interpretuje jednosměrnou relaci jako 1:N. Pokud například chcete vztah 1:1 mezi instruktorem a přiřazením OfficeAssignment, kde máte navigační vlastnost pouze u typu instruktora, musíte pro konfiguraci této relace použít rozhraní API fluent.

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

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

Povolení kaskádového odstranění

Kaskádové odstranění relace můžete nakonfigurovat pomocí metody WillCascadeOnDelete. Pokud cizí klíč závislé entity není nullable, pak Code First nastaví kaskádové odstranění relace. Pokud je cizí klíč závislé entity s možnou hodnotou null, program Code First nenastaví kaskádové odstranění relace a při odstranění objektu zabezpečení se cizí klíč nastaví na hodnotu null.

Tyto kaskádové konvence odstraňování můžete odebrat pomocí:

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

Následující kód nakonfiguruje požadovaný vztah a pak kaskádové odstranění zakáže.

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

Konfigurace složeného cizího klíče

Pokud se primární klíč typu Oddělení skládá z vlastností DepartmentID a Name, nakonfigurujete primární klíč pro oddělení a cizí klíč pro typy kurzů následujícím způsobem:

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

Přejmenování cizího klíče, který není definován v modelu

Pokud se rozhodnete nedefinovat cizí klíč pro typ CLR, ale chcete zadat, jaký název má mít v databázi, postupujte takto:

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

Konfigurace názvu cizího klíče, který neodpovídá první konvenci kódu

Pokud vlastnost cizího klíče u třídy Course byla volána SomeDepartmentID místo DepartmentID, budete muset provést následující kroky, abyste určili, že Má být Parametr SomeDepartmentID cizím klíčem:

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

Model použitý v ukázkách

Následující model Code First se používá pro ukázky na této stránce.

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