Freigeben über


Fluent-API – Beziehungen

Hinweis

Diese Seite enthält Informationen zum Einrichten von Beziehungen in Ihrem Code First-Modell mithilfe der Fluent-API. Allgemeine Informationen zu Beziehungen in EF und zum Zugreifen auf und Bearbeiten von Daten mithilfe von Beziehungen finden Sie unter Beziehungen & Navigationseigenschaften.

Wenn Sie mit Code First arbeiten, definieren Sie Ihr Modell, indem Sie Ihre Domänen-CLR-Klassen definieren. Standardmäßig verwendet Entity Framework die Code First-Konventionen, um Ihre Klassen dem Datenbankschema zuzuordnen. Wenn Sie die Code First-Benennungskonventionen verwenden, können Sie in den meisten Fällen auf Code First zurückgreifen, um Beziehungen zwischen Ihren Tabellen basierend auf den Fremdschlüsseln und Navigationseigenschaften einzurichten, die Sie für die Klassen definieren. Wenn Sie die Konventionen beim Definieren Ihrer Klassen nicht befolgen oder die Funktionsweise der Konventionen ändern möchten, können Sie die Fluent-API oder Datenanmerkungen verwenden, um Ihre Klassen so zu konfigurieren, dass Code First die Beziehungen zwischen Ihren Tabellen zuordnen kann.

Einführung

Beim Konfigurieren einer Beziehung mit der Fluent-API beginnen Sie mit der EntityTypeConfiguration-Instanz und verwenden dann die HasRequired-, HasOptional- oder HasMany-Methode, um den Typ der Beziehung anzugeben, an der diese Entität teilnimmt. Die Methoden HasRequired und HasOptional verwenden einen Lambda-Ausdruck, der eine Referenznavigationseigenschaft darstellt. Die HasMany-Methode verwendet einen Lambda-Ausdruck, der eine Sammlungsnavigationseigenschaft darstellt. Anschließend können Sie eine umgekehrte Navigationseigenschaft mithilfe der Methoden WithRequired, WithOptional und WithMany konfigurieren. Diese Methoden weisen Überladungen auf, die keine Argumente annehmen und zum Angeben der Kardinalität mit unidirektionalen Navigationen verwendet werden können.

Anschließend können Sie Fremdschlüsseleigenschaften mithilfe der HasForeignKey-Methode konfigurieren. Diese Methode verwendet einen Lambda-Ausdruck, der die Eigenschaft darstellt, die als Fremdschlüssel verwendet werden soll.

Konfigurieren einer erforderlichen bis optionalen Beziehung (1:0 oder 1)

Im folgenden Beispiel wird eine 1:0-oder-1-Beziehung konfiguriert. Das OfficeAssignment verfügt über die InstructorID-Eigenschaft, die sowohl Primärschlüssel als auch Fremdschlüssel ist. Da der Name der Eigenschaft nicht der Konvention folgt, wird die HasKey-Methode verwendet, um den Primärschlüssel zu konfigurieren.

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

Konfigurieren einer Beziehung, bei der beide Enden erforderlich sind (1:1)

In den meisten Fällen kann Entity Framework ableiten, welcher Typ abhängig ist und welches der Prinzipal in einer Beziehung ist. Wenn jedoch beide Enden der Beziehung erforderlich sind oder beide Seiten optional sind, kann Entity Framework die abhängige und die Hauptentität nicht identifizieren. Wenn beide Enden der Beziehung erforderlich sind, verwenden Sie WithRequiredPrincipal oder WithRequiredDependent nach der HasRequired-Methode. Wenn beide Enden der Beziehung optional sind, verwenden Sie WithOptionalPrincipal oder WithOptionalDependent nach der HasOptional-Methode.

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

Konfigurieren einer Viele-zu-Viele-Beziehung

Der folgende Code konfiguriert eine Viele-zu-Viele-Beziehung zwischen den Kurs- und Kursleitertypen. Im folgenden Beispiel werden die standardmäßigen Code First-Konventionen verwendet, um eine Verknüpfungstabelle zu erstellen. Daher wird die CourseInstructor-Tabelle mit Course_CourseID und Instructor_InstructorID Spalten erstellt.

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

Wenn Sie den Verknüpfungstabellennamen und die Namen der Spalten in der Tabelle angeben möchten, müssen Sie mithilfe der Map-Methode zusätzliche Konfigurationen ausführen. Der folgende Code generiert die Tabelle "CourseInstructor" mit den Spalten "CourseID" und "InstructorID".

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

Konfigurieren einer Beziehung mit einer Navigationseigenschaft

Eine einseitige (auch als unidirektionale) Beziehung liegt vor, wenn eine Navigationseigenschaft nur für einen der Beziehungsendpunkte definiert ist und nicht für beide. Üblicherweise interpretiert Code First immer eine unidirektionale Beziehung als eins-zu-viele. Wenn Sie z. B. eine 1:1-Beziehung zwischen Instructor und OfficeAssignment verwenden möchten, bei der Sie über eine Navigationseigenschaft nur für den Kursleitertyp verfügen, müssen Sie die Fluent-API verwenden, um diese Beziehung zu konfigurieren.

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

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

Aktivieren der Löschweitergabe

Sie können die Löschweitergabe für eine Beziehung mithilfe der WillCascadeOnDelete-Methode konfigurieren. Wenn ein Fremdschlüssel für die abhängige Entität nicht nullfähig ist, aktiviert Code First die Löschweitergabe für die Beziehung. Wenn ein Fremdschlüssel für die abhängige Entität nullable ist, legt Code First kein kaskadierendes Löschen für die Beziehung fest, und wenn der Prinzipal gelöscht wird, wird der Fremdschlüssel auf NULL festgelegt.

Sie können diese Kaskadenlöschkonventionen entfernen, indem Sie Folgendes verwenden:

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

Der folgende Code konfiguriert die erforderliche Beziehung und deaktiviert dann die Löschweitergabe.

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

Konfigurieren eines zusammengesetzten Fremdschlüssels

Wenn der Primärschlüssel des Typs "Department" aus den Eigenschaften "DepartmentID" und "Name" bestand, konfigurieren Sie den Primärschlüssel für die Abteilung und den Fremdschlüssel für die Kurstypen wie folgt:

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

Umbenennen eines Fremdschlüssels, der im Modell nicht definiert ist

Wenn Sie sich entscheiden, keinen Fremdschlüssel für den CLR-Typ zu definieren, aber den Namen in der Datenbank angeben möchten, gehen Sie wie folgt vor:

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

Konfigurieren eines Fremdschlüsselnamens, der nicht der ersten Codekonvention entspricht

Wenn die Fremdschlüsseleigenschaft der Kursklasse "SomeDepartmentID" anstelle von DepartmentID genannt wurde, müssen Sie folgendes ausführen, um anzugeben, dass SomeDepartmentID der Fremdschlüssel sein soll:

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

In Beispielen verwendetes Modell

Das folgende Code First-Modell wird für die Beispiele auf dieser Seite verwendet.

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