本文深入探討 ORM 中繼承關聯的四種映射策略:
所謂單表繼承,即用一張資料表儲存整個繼承體系中所有實體的資料。適合實體數量較少、總筆數較少、子類別對父類別屬性擴展較少的情境。
[Table("AllInOneTable")]
public interface Parent : IEntity
{
[PrimaryKey]
int ID { get; }
string Name { get; set; }
}
[Table("AllInOneTable")]
public interface AnotherParent : IEntity
{
[PrimaryKey]
int ID { get; }
int Age { get; set; }
}
[Table("AllInOneTable")]
public interface Child : Parent, AnotherParent
{
[PrimaryKey]
new int ID { get; set; }
DateTime Birthday { get; set; }
}
注意:若多個基底介面包含同名屬性,需使用 new 關鍵字避免編譯錯誤。
實務上需透過 AdditionalWhere 與 AdditionalInsert 區分子類別資料。範例如下:
public interface Message : IEntity
{
[PrimaryKey]
int ID { get; }
string Title { get; set; }
string Content { get; set; }
DateTime UpdateTime { get; set; }
}
[Table("Message", AdditionalInsert = "[MessageType] = 1", AdditionalWhere = "[MessageType] = 1")]
public interface CommonMessage : Message
{
int UserID { get; set; }
}
[Table("Message", AdditionalInsert = "[MessageType] = 2", AdditionalWhere = "[MessageType] = 2")]
public interface SpecialMessage : Message
{
int GroupID { get; set; }
}
新增 MessageType 欄位區分資料類型:1 代表 CommonMessage,2 代表 SpecialMessage。查詢時自動套用 AdditionalWhere,插入時自動帶入 AdditionalInsert 指定的預設值。
每個實體(含基類別)各自對應一張完整資料表,子類別資料表包含父類別所有欄位。優點是查詢單一實體無需 Join,缺點是修改基類別結構需同步更新所有子類別資料表。
基類別對應一張主表,子類別各自對應一張擴展表,透過主鍵關聯。優點是符合正規化,缺點是查詢需 Join 多張表,效能較差。
介面映射較靈活,支援多重繼承;基類繼承映射較直觀,但受限於單一繼承。選擇取決於領域模型複雜度與 ORM 框架支援度。