🧾EF Core:Fluent API 與 Data Annotations 有什麼不同?一篇就搞懂(含範例與選用指南)

 

內容

想把 C# 類別正確對應到資料庫表(Code-First),你一定會遇到 Data AnnotationsFluent API。這兩者其實都是在做「模型組態」(Model Configuration):也就是告訴 EF Core 你的類別如何對映到資料表。差別在於「寫法位置」與「可表達能力」。

TL;DR(先給結論)

  • Data Annotations:直接在類別/屬性貼 Attribute(如 [Key], [Required], [MaxLength])。上手快、就地可讀,但表達力有限。

  • Fluent API:在 OnModelCreatingIEntityTypeConfiguration<T> 裡用鏈式語法設定。最完整、可抽離、可維護,適合中大型專案。

  • 衝突優先序:慣例 < Data Annotations < Fluent API(Fluent 會覆蓋註解)。


什麼是 Data Annotations?

把規則貼在模型本身,例如必填、長度、欄位型別、索引等。可讀性高、看檔案就知道欄位規則。

using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; [Table("CUSTOMERS")] public class Customer { [Key] public int Id { get; set; } [Required, MaxLength(100)] public string Name { get; set; } = string.Empty; [Column(TypeName = "varchar(50)")] public string? Email { get; set; } public ICollection<Order> Orders { get; set; } = new List<Order>(); } [Table("ORDERS")] public class Order { [Key] public int Id { get; set; } [Required] public DateTime CreatedAt { get; set; } public int CustomerId { get; set; } [ForeignKey(nameof(CustomerId))] public Customer Customer { get; set; } = null!; }

小提醒:EF Core 也提供 [Index], [Precision] 等新特性,但複合鍵、刪除行為、擁有型別(Owned Types)、全域查詢篩選等進階設定,還是建議用 Fluent API。


什麼是 Fluent API?

把組態集中在一處OnModelCreating 或分檔 IEntityTypeConfiguration<T>):

using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; public class CustomerConfig : IEntityTypeConfiguration<Customer> { public void Configure(EntityTypeBuilder<Customer> b) { b.ToTable("CUSTOMERS"); b.HasKey(x => x.Id); b.Property(x => x.Name) .IsRequired() .HasMaxLength(100); b.Property(x => x.Email) .HasColumnType("varchar(50)"); b.HasIndex(x => x.Email).IsUnique(); b.HasMany(x => x.Orders) .WithOne(x => x.Customer) .HasForeignKey(x => x.CustomerId) .OnDelete(DeleteBehavior.Restrict); } } public class OrderConfig : IEntityTypeConfiguration<Order> { public void Configure(EntityTypeBuilder<Order> b) { b.ToTable("ORDERS"); b.HasKey(x => x.Id); b.Property(x => x.CreatedAt).IsRequired(); } } public class AppDbContext : DbContext { protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfiguration(new CustomerConfig()); modelBuilder.ApplyConfiguration(new OrderConfig()); } }

只有 Fluent 才好寫的事:

  • 複合主鍵:b.HasKey(x => new { x.Sku, x.Site });

  • 進階關聯與刪除行為:OnDelete(DeleteBehavior.Restrict/Cascade/NoAction)

  • 擁有型別(Value Object):b.OwnsOne(...)

  • 欄位轉換:HasConversion()(enum↔字串/數值、JSON、逗號字串等)

  • 全域查詢篩選:HasQueryFilter(x => !x.IsDeleted)

  • 預設值/計算欄位 SQL:HasDefaultValueSql(...)HasComputedColumnSql(...)


快速比較表

面向 Data Annotations Fluent API
可表達能力 基本欄位/索引/必要性/長度 幾乎全部(關聯、刪除行為、複合鍵、擁有型別、轉換、全域篩選…)
可讀性 規則貼在類別上,一眼可見 規則集中、乾淨,結構化管理
關注點分離 較弱(模型與對映耦合) 較強(可分檔、可重用)
複雜關聯 受限/不便 直覺完整
跨專案重用 不易 容易(IEntityTypeConfiguration<T>
衝突時優先序 慣例 < 註解 Fluent 最高,會覆蓋註解

我應該選哪個?

  • 小型/快速原型:以 Data Annotations 為主,省時、直覺。

  • 中大型/多表關聯/長期維護:以 Fluent API 為主,彈性最大、可抽離組態。

  • 混合策略(常見實務)

    • 基礎屬性([Required], [MaxLength], [Index])用註解保留就地可讀;

    • 關聯、複合鍵、刪除行為、轉換、Owned Types、Query Filter等用 Fluent 集中管理。

    • 有衝突時,以 Fluent 為準


進階小範例(抄了就能用)

1) 複合鍵+唯一索引

b.HasKey(x => new { x.Site, x.Sku }); b.HasIndex(x => new { x.Site, x.Sku, x.Week }).IsUnique();

2) Enum ↔ 字串轉換

b.Property(x => x.Status) .HasConversion<string>() // Enum 存字串 .HasMaxLength(20) .IsRequired();

3) 擁有型別(Value Object)

b.OwnsOne(x => x.Address, a => { a.Property(p => p.City).HasMaxLength(50); a.Property(p => p.Zip).HasMaxLength(10); });

4) 全域查詢篩選(軟刪除)

modelBuilder.Entity<Customer>() .HasQueryFilter(c => !c.IsDeleted);

5) 預設值與計算欄位

b.Property(x => x.CreatedAt) .HasDefaultValueSql("CURRENT_TIMESTAMP"); b.Property(x => x.Total) .HasComputedColumnSql("[UnitPrice]*[Qty]");

若使用 Oracle、PostgreSQL 等,記得把 HasColumnType(...) 及 SQL 片段改成對應資料庫語法。


常見陷阱與最佳實務

  1. 把所有規則都塞進模型 → 長期會變得難維護。建議進階設定集中到 Fluent。

  2. 跨檔案/跨專案重用 → 用 IEntityTypeConfiguration<T> 把組態拆檔,未來擴充更容易。

  3. 命名/型別差異 → 明確指定 HasColumnType、長度與精度(如 HasPrecision(18, 4))。

  4. 刪除行為 → 預設可能是 Cascade;重要資料建議明確指定 RestrictNoAction

  5. 衝突時誰說了算 → 記得 Fluent > Data Annotations > 慣例


結語

Data Annotations 讓你快速上手、文件化欄位規則;Fluent API 則提供完整控制與可維護結構。實務上很常「混搭」:簡單規則留在模型,複雜對映集中到 Fluent。掌握這個分工,你的 EF Core 專案會更乾淨、穩定,也更容易交接維護。

留言

這個網誌中的熱門文章

🔍Vue.js 專案錯誤排查:解決 numericFields is not defined 與合併儲存格邏輯最佳化

🔎EF Core 連 Oracle 出現 ORA-00600 [kpp_concatq:2] 的完整排錯指南(含 EF Core ToString/CultureInfo 錯誤)

🛠【ASP.NET Core + Oracle】解決 ORA-00904 "FALSE": 無效的 ID 錯誤與資料欄位動態插入顯示問題