LINQ to SQL 僅支援 **單表繼承 (Table Per Hierarchy, TPH)**:整個繼承體系對應單一資料表,透過 **判別欄位** 區分實體型別。本文以 Vehicle/Car/Truck 經典範例,完整示範 TPH 映射配置、查詢與實體具現化技巧。
| 欄位 | 型別 | 說明 |
|---|---|---|
| VIN | PK, varchar | 車輛識別碼 |
| Key | varchar(1) | 判別欄位: V=Vehicle, C=Car, T=Truck |
| MfgPlant | varchar | 製造廠 (基類共用) |
| TrimCode | int | Car 專屬 |
| ModelName | varchar | Car 專屬 |
| Tonnage | int | Truck 專屬 |
| Axles | int | Truck 專屬 |

[Table(Name = "Vehicle")]
[InheritanceMapping(Code = "V", Type = typeof(Vehicle), IsDefault = true)]
[InheritanceMapping(Code = "C", Type = typeof(Car))]
[InheritanceMapping(Code = "T", Type = typeof(Truck))]
public class Vehicle
{
[Column(IsDiscriminator = true, Name = "Key")] // 判別欄位
public string Key { get; set; }
[Column(IsPrimaryKey = true, Name = "VIN")]
public string VIN { get; set; }
[Column(Name = "MfgPlant")]
public string MfgPlant { get; set; }
}
public class Car : Vehicle
{
[Column(Name = "TrimCode")]
public int TrimCode { get; set; }
[Column(Name = "ModelName")]
public string ModelName { get; set; }
}
public class Truck : Vehicle
{
[Column(Name = "Tonnage")]
public int Tonnage { get; set; }
[Column(Name = "Axles")]
public int Axles { get; set; }
}
[Table]:僅基類標註,對應單一資料表。[InheritanceMapping]:每個具象類別一條,**Code** 對應判別欄位值,**Type** 對應 CLR 型別,**IsDefault=true** 表示判別值不符合任何映射時的 fallback 型別 (此處為 Vehicle)。[Column(IsDiscriminator=true)]:標記判別欄位,**必須存在於基類**,且型別支援字串/數值/布林。[Table],自動繼承基類表映射。
[Database(Name = "MyDatabase")]
public class VehicleContext : DataContext
{
public VehicleContext(string connection) : base(connection) { }
// 基類表:包含所有型別
public Table<Vehicle> Vehicles => GetTable<Vehicle>();
// 型別化查詢屬性:利用 OfType<T>()
public IQueryable<Car> Cars => Vehicles.OfType<Car>();
public IQueryable<Truck> Trucks => Vehicles.OfType<Truck>();
}
// 方式 1: Where + 型別測試
return Vehicles.Where(v => v is Car);
// 方式 2: Select + Cast + Where
return Vehicles.Select(v => v as Car).Where(c => c != null);
💡OfType<T>()最簡潔且語意明確,會轉譯為 SQLWHERE Key = 'C'。
using (var ctx = new VehicleContext(connStr))
{
// 查詢所有 Car
foreach (var car in ctx.Cars)
Console.WriteLine($"Car: {car.VIN} {car.ModelName}");
// 查詢所有 Truck
foreach (var truck in ctx.Trucks)
Console.WriteLine($"Truck: {truck.VIN} {truck.Tonnage}");
// 新增 Car
var newCar = new Car { VIN = "CAR001", Key = "C", MfgPlant = "PlantA", ModelName = "Sedan", TrimCode = 1 };
ctx.Vehicles.InsertOnSubmit(newCar);
ctx.SubmitChanges();
}

| 優點 | 缺點 |
|---|---|
| 查詢無需 JOIN,效能極佳 | 子類別專屬欄位大量 NULL,浪費儲存空間 |
| Schema 簡單,單表維護 | 無法在資料庫層級對子類別欄位加 NOT NULL 約束 |
| 多型查詢自然 (OfType) | 繼承層級深/寬時,單表欄位爆炸 |
| 實體具現化邏輯單一 | 不支援 TPT (Table Per Type) / TPC (Table Per Concrete) |
LINQ to SQL 的 TPH 映射機制簡潔有力:[InheritanceMapping] + IsDiscriminator + OfType<T>() 三大招式即可掌控。雖受限於單表架構,但在「繼承層級淺、子類別欄位少、查詢效能優先」的場景下,TPH 仍是極具性價比的選擇。若專案演進需更豐富的 ORM 能力,平滑遷移至 Entity Framework 是自然演進路徑。