共用方式為


Fluent API - 關聯性

注意

此頁面提供使用 Fluent API 在 Code First 模型中設定關聯性的相關信息。 如需 EF 中關聯性的一般資訊,以及如何使用關聯性來存取及操作數據,請參閱 關聯性與導覽屬性

使用 Code First 時,您可以藉由定義網域 CLR 類別來定義模型。 根據預設,Entity Framework 會使用 Code First 慣例,將類別對應至資料庫架構。 如果您使用 Code First 命名慣例,在大部分情況下,您可以依賴 Code First 來根據您在類別上定義的外鍵和導覽屬性來設定資料表之間的關聯性。 如果您在定義類別時未遵循慣例,或想要變更慣例的運作方式,您可以使用 Fluent API 或數據批注來設定類別,讓 Code First 可以對應數據表之間的關聯性。

簡介

使用 Fluent API 設定關聯性時,您會從 EntityTypeConfiguration 實例開始,然後使用 HasRequired、HasOptional 或 HasMany 方法來指定此實體參與的關聯性類型。 HasRequired 和 HasOptional 方法會採用代表參考導覽屬性的 Lambda 表達式。 HasMany 方法會採用代表集合導覽屬性的 Lambda 表達式。 接著,您可以使用WithRequired、WithOptional和WithMany方法來設定反嚮導覽屬性。 這些方法具有不採用自變數的多載,而且可用來使用單嚮導覽來指定基數。

接著,您可以使用 HasForeignKey 方法來設定外鍵屬性。 這個方法會採用 Lambda 運算式,表示要當做外鍵使用的屬性。

設定必要對選擇性關聯性 (一對零或一)

下列範例會設定一對零或一關聯性。 OfficeAssignment 具有是主鍵和外鍵的 InstructorID 屬性,因為屬性的名稱未遵循 HasKey 方法用來設定主鍵的慣例。

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

設定需要兩端的關聯性 (一對一)

在大部分情況下,Entity Framework 可以推斷哪一種類型是相依的,而哪一種是關聯性中的主體。 不過,當兩端都需要關聯性,或兩端都是選擇性的 Entity Framework 無法識別相依和主體時。 需要兩端關聯性時,請在 HasRequired 方法後面使用 WithRequiredPrincipal 或 WithRequiredDependent。 當關聯性的兩端都是選擇性的時,請在 HasOptional 方法後面使用 WithOptionalPrincipal 或 WithOptionalDependent。

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

設定多對多關聯性

下列程式代碼會設定 Course 與 Instructor 類型之間的多對多關聯性。 在下列範例中,會使用預設 Code First 慣例來建立聯結數據表。 因此,CourseInstructor 數據表會使用Course_CourseID和Instructor_InstructorID數據行來建立。

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

如果您想要指定聯結數據表名稱和數據表中的數據行名稱,則需要使用 Map 方法執行其他設定。 下列程式代碼會產生具有 CourseID 和 InstructorID 數據行的 CourseInstructor 數據表。

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

使用一個導覽屬性設定關聯性

單向(也稱為單向)關聯性是在只有其中一個關聯性結尾上定義導覽屬性,而不是同時定義兩者時。 依照慣例,Code First 一律會將單向關聯性解譯為一對多。 例如,如果您想要 Instructor 與 OfficeAssignment 之間的一對一關聯性,其中只有 Instructor 類型的導覽屬性,您必須使用 Fluent API 來設定此關聯性。

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

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

啟用串聯刪除

您可以使用WillCascadeOnDelete方法,在關聯性上設定串聯刪除。 如果相依實體上的外鍵不可為 Null,則 Code First 會在關聯性上設定串聯刪除。 如果相依實體上的外鍵可為 Null,Code First 不會在關聯性上設定串聯刪除,而且當主體刪除時,外鍵會設定為 Null。

您可以使用下列方式移除這些串聯刪除慣例:

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

下列程式代碼會將關聯性設定為必要,然後停用串聯刪除。

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

設定複合外鍵

如果 Department 類型的主鍵是由 DepartmentID 和 Name 屬性所組成,您會在 Course 類型上設定主鍵和外鍵,如下所示:

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

重新命名模型中未定義的外鍵

如果您選擇不要在 CLR 類型上定義外鍵,但想要指定它在資料庫中應該擁有的名稱,請執行下列動作:

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

設定未遵循程式代碼第一慣例的外鍵名稱

如果 Course 類別上的外鍵屬性稱為 SomeDepartmentID,而不是 DepartmentID,您必須執行下列動作,以指定您希望 SomeDepartmentID 成為外鍵:

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

範例中使用的模型

下列 Code First 模型會用於此頁面上的範例。

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