EF Core中通過Fluent API完成對錶的配置

wujuncheng發表於2021-03-07

EF Core中通過Fluent API完成對錶的配置

設定實體在資料庫中的表名

通過ToTable可以為資料模型在資料庫中自定義表名,如果不配置,則表名為模型名的複數形式

public class EmployeeConfig:IEntityTypeConfiguration<Employee>
{
    public void Configure(EntityTypeBuilder<Employee> builder)
    {
        // 預設情況下,Employee實體在資料庫中會生成Employees的資料表,這裡通過ToTable(),將其指定為Employee
        builder.ToTable("Employee");
    }
}

表間關係對映

在EF Core中,通過Fluent API做表間關係對映時,可以將API分為兩類兩種

兩類:has和with

三種:One、Many

通過兩類兩種的組合,就可以完成絕大多數表間關係的對映,下面放一些常用的關係配置

public class EmployeeConfig:IEntityTypeConfiguration<Employee>
{
    public void Configure(EntityTypeBuilder<Employee> builder)
    {
        builder.ToTable(nameof(Employee));
		// 配置Employee表有一個Department物件,Department有多個Employee物件
        // 這是典型的一對多配置
        builder.HasOne(e => e.Department).WithMany(d=>d.Employees);
    }
}

通常配置一對多的時候只需要在一張表上進行配置就可以了,但也可以在兩張表上都進行配置,這樣更清晰

public class DepartmentConfig:IEntityTypeConfiguration<Department>
{
    public void Configure(EntityTypeBuilder<Department> builder)
    {
        builder.ToTable(nameof(Department));

        // 配置Department表有多個Employee物件,Employee有一個Department物件
        builder.HasMany(d => d.Employees).WithOne(e=>e.Department);

        // 配置Department表有一個領導(也是Employee物件),領導也屬於一個部門
        builder.HasOne(d => d.Leader).WithOne(e => e.Department);
    }
}

在表關係的配置中,遵循 Has…( ).With…( )的配置方式,Has指的是配置的這張表,然後通過lambda表示式來指定對應的屬性,如上面的Department表,通過lambda表示式 d => d.Employees 指定要配置的欄位是Employess,HasMany則是指Department表的Employees對應多個

With則是前面has指定的屬性對應的表,WithOne反過來指定了Employee表中的Department欄位是一個

所以這裡就是一個多對一的配置

通過這四個單詞的組合,就可以完成一對多、一對一、多對多(通過中間表拆分成兩個一對多)的表間關係配置

這裡將Entity檔案放出來,以便更好理解

public class Employee
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public bool Gender { get; set; }
    public string Phone { get; set; }
    public decimal Rating { get; set; }
    public decimal Salary { get; set; }
    public Guid DepartmentId { get; set; }
    public Department Department { get; set; }
}

public class Department
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public Guid LeaderId { get; set; }
    public Employee Leader { get; set; }
    public List<Employee> Employees { get; set; }
    public string NoUse { get; set; }
}

主鍵設定

EF Core會自動將實體中名為Id的屬性設定為主鍵,但是有時候主鍵並不是這麼規整的命名,或者需要聯合主鍵的情況,就需要手動指定主鍵

public class DepartmentConfig:IEntityTypeConfiguration<Department>
{
    public void Configure(EntityTypeBuilder<Department> builder)
    {
        // 配置資料庫中主鍵為實體的Id欄位
        builder.HasKey(d => d.Id); 
    }
}

這裡指定了Department的Id欄位為主鍵,實體屬性名為Id不用手動指定,這裡只是展示一下自定義的語法

public class DepartmentConfig:IEntityTypeConfiguration<Department>
{
    public void Configure(EntityTypeBuilder<Department> builder)
    {
        // 配置資料庫中主鍵為實體的Id和Name的聯合主鍵
        builder.HasKey(d => new {d.Id, d.Name});
    }
}

聯合主鍵的設定也很簡單,通過new一個匿名物件的方式提供

主鍵的值

EF Core會預設為主鍵生成值,但有時候我們希望使用主鍵並且自己自定義相關的值,比如說自選賬號、課程Id等,這時可以這樣配置

public class DepartmentConfig:IEntityTypeConfiguration<Department>
{
    public void Configure(EntityTypeBuilder<Department> builder)
    {
        builder.Property(d => d.Id)
        .ValueGeneratedNever(); // 配置資料庫中實體的Id欄位不自動生成值
    }
}

通過ValueGeneratedNever()可以禁用自動生成值,實際上它可以給任何一個屬性都配置,但是通常只有主鍵是預設生成值的

外來鍵設定

如果有外來鍵則必須有另一個與之關聯的表,所以外來鍵配置只能在表關係配置後附加

EF Core會預設將關聯表+Id的欄位設定為外來鍵,同主鍵一樣,有時候不是那麼規整,就需要手動指定

public class DepartmentConfig:IEntityTypeConfiguration<Department>
{
    public void Configure(EntityTypeBuilder<Department> builder)
    {
        builder.HasOne(d => d.Leader).WithOne(e => e.Department).HasForeignKey("LeaderId");
    }
}

在Department實體中指定了Leader,Leader也是Employee物件,如果依照約定屬性為EmployeeId會自定設定為外來鍵欄位,但是這裡指定了LeaderId,就需要手動設定外來鍵欄位為LeaderId

忽略某個欄位在資料庫中的對映

public class DepartmentConfig:IEntityTypeConfiguration<Department>
{
    public void Configure(EntityTypeBuilder<Department> builder)
    {
        // 資料庫中忽略該欄位,該欄位不會存在該實體屬性對應的欄位
        builder.Ignore(d => d.NoUse); 
    }
}

前面Department實體中有一個NoUse欄位,但是不希望它在資料庫中對映該欄位,就可以通過Ignore的方式忽略掉

欄位約束

通過Fluent API能夠對欄位進行約束,這樣在生成資料庫表時就會將相應的約束生成,如設定了欄位的最大長度在資料庫表中的欄位資料型別時nvarchar(設定的最大長度),如果沒有設定,在資料庫表中的欄位資料型別則是nvarchar(max)

Fluent支援流式語法,可以將多個約定流式附加

非空約束
public class DepartmentConfig:IEntityTypeConfiguration<Department>
{
    public void Configure(EntityTypeBuilder<Department> builder)
    {
        // 設定姓名欄位不為空
        builder.Property(d => d.Name).IsRequired();
    }
}
欄位最大長度
public class DepartmentConfig:IEntityTypeConfiguration<Department>
{
    public void Configure(EntityTypeBuilder<Department> builder)
    {
        // 設定姓名欄位最大長度為30,且不為空
        builder.Property(d => d.Name).HasMaxLength(30).IsRequired();
    }
}
固定長度欄位
public class EmployeeConfig:IEntityTypeConfiguration<Employee>
{
    public void Configure(EntityTypeBuilder<Employee> builder)
    {
        // Phone欄位在資料庫中為11位固定長度字串 IsFixedLength()用於指定該欄位是否為固定長度
        builder.Property(e => e.Phone).HasMaxLength(11).IsFixedLength();
    }
}

IsFixedLength()用於指定該欄位是否為固定長度,其長度為前面設定的欄位最大長度

指定欄位名
public class EmployeeConfig:IEntityTypeConfiguration<Employee>
{
    public void Configure(EntityTypeBuilder<Employee> builder)
    {
        // 顯式指定實體屬性對應資料庫中的欄位名,這裡指定Phone欄位對應的資料庫欄位為ChinaPhone
        builder.Property(e => e.Phone).HasColumnName("ChinaPhone");
    }
}
指定資料型別
public class EmployeeConfig:IEntityTypeConfiguration<Employee>
{
    public void Configure(EntityTypeBuilder<Employee> builder)
    {
        // 指定decimal的精度為5刻度為2
        builder.Property(e => e.Rating).HasColumnType("decimal(5, 2)");
    }
}

注:關於精度和刻度的解釋,精度是數字的位數,刻度是小數的位數,即decimal(5, 2)能表示的最大數是999.99,一共五位,小數兩位

指定資料型別更常用的情況是將實體的decimal型別指定為資料庫中的money型別

public class EmployeeConfig:IEntityTypeConfiguration<Employee>
{
    public void Configure(EntityTypeBuilder<Employee> builder)
    {
        // 指定decimal的精度為5刻度為2
        builder.Property(e => e.Salary).HasColumnType("money");
    }
}

為什麼我的Fluent API配置長這樣

因為進行了分組配置,將每個類的配置分別拆分到不同的檔案

具體的配置可以看看微軟的官方文件

分組配置

相關文章