EF4 不原生支援枚舉屬性映射 (EF5+ 才支援)。通過 Complex Type (復雜類型) 包裝枚舉,實現 int 資料庫列與 Enum 領域模型的雙向無縫轉換,且保持 LINQ 查詢可翻譯性。本文給出生產就緒的完整模式代碼。
// 1. 定義枚舉
public enum AccountRoleEnum { Admin = 1, User = 2 }
// 2. 複雜類型包裝器 (必須為 class、非 struct、含無參構造函數)
public class RoleWrapper
{
private AccountRoleEnum _value;
// 資料庫映射用 int 屬性
public int Value
{
get => (int)_value;
set => _value = (AccountRoleEnum)value;
}
// 領域代碼用強類型屬性
public AccountRoleEnum EnumValue
{
get => _value;
set => _value = value;
}
// 隱式轉換:枚舉 ⇄ 包裝器
public static implicit operator RoleWrapper(AccountRoleEnum role)
=> new RoleWrapper { EnumValue = role };
public static implicit operator AccountRoleEnum(RoleWrapper wrapper)
=> wrapper?.EnumValue ?? AccountRoleEnum.User; // null 保護
}
public class Account
{
public Guid ID { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string Email { get; set; }
// EF 映射此複雜類型 (資料庫僅存 Value int 列)
public RoleWrapper Role { get; set; }
}
RoleWrapper,屬性 Value (Int32)。Account 添加複雜類型屬性 Role。Role.Value 映射到資料表 Role 列。public class EntitiesContext : ObjectContext
{
public EntitiesContext() : base("name=Entities", "Entities")
{
_accounts = CreateObjectSet<Account>();
}
public ObjectSet<Account> Accounts => _accounts;
private ObjectSet<Account> _accounts;
}
// 使用示例
using (var ctx = new EntitiesContext())
{
// 創建
ctx.Accounts.AddObject(new Account
{
ID = Guid.NewGuid(),
UserName = "admin",
Role = AccountRoleEnum.Admin // 隱式轉換為 RoleWrapper
});
ctx.SaveChanges();
// 查詢:領域代碼用 EnumValue
var admins = ctx.Accounts.Where(a => a.Role.EnumValue == AccountRoleEnum.Admin).ToList();
// LINQ to Entities 查詢:必須用 Value (可翻譯為 SQL)
var adminsSql = ctx.Accounts.Where(a => a.Role.Value == (int)AccountRoleEnum.Admin).ToList();
}
| 場景 | 正確寫法 | 錯誤寫法 |
|---|---|---|
| 內存比較 | account.Role == AccountRoleEnum.Admin (隱式轉換) | account.Role.EnumValue == ... 亦可但冗餘 |
| LINQ to Entities 查詢 | .Where(a => a.Role.Value == (int)AccountRoleEnum.Admin) | .Where(a => a.Role.EnumValue == ...) 將拋出無法翻譯異常 |
| 資料庫遷移/腳本 | 列類型 INT NOT NULL,預設值 2 (User) | 嘗試建立 ENUM 類型 (SQL Server 不支援) |
public class EnumWrapper<TEnum> where TEnum : struct, Enum
{
private TEnum _value;
public int Value { get => (int)(object)_value; set => _value = (TEnum)(object)value; }
public TEnum EnumValue { get => _value; set => _value = value; }
public static implicit operator EnumWrapper<TEnum>(TEnum v) => new EnumWrapper<TEnum> { EnumValue = v; }
public static implicit operator TEnum(EnumWrapper<TEnum> w) => w?.EnumValue ?? default;
}
// 使用
public class Account { public EnumWrapper<AccountRoleEnum> Role { get; set; } }
注意:EF4 需在 EDMX 中為每個具體泛型實例 (如 EnumWrapper`1[[AccountRoleEnum]]) 定義複雜類型,Code First 自動處理。
EF5 原生支援 enum AccountRoleEnum : int 直接映射。升級步驟:
RoleWrapper / EnumWrapper。public AccountRoleEnum Role { get; set; }。int 不變。.Value 強轉,直接用枚舉查詢 (EF5+ 支援翻譯)。Complex Type 包裝模式讓 EF4 專案在不升級框架前提下,享受枚舉的強類型、可讀性與編譯期安全,是遺留系統現代化的最佳過渡方案。