假设有这样一个枚举:
/// <summary> /// 字典项类型 /// </summary> public enum DicItemType { [EnumDescription("程序使用")] Program = 0, [EnumDescription("用户自定义")] Custom = 1 }
NHibernate默认是映射为数据库中的数字类型,也就是0或者1。当我们使用数据库管理工具(例如PLSql/Developer)直接浏览数据库里的数据时,看到的一排又一排的0或者1,可读性不太好,总是要再去找枚举定义看0代表的是什么,1代表的又是什么。当使用存储过程写一些复杂查询时,也会写 CASE WHEN ITEM_TYPE = 1 THEN ... 或者 WHERE ITEM_TYPE = 1 这样包含了神奇数字的语句,可读性同样不好。虽然可以在后面跟上注释,也还是很烦。能不能让NHibernate把枚举保存为数据库里的字符串呢?例如不是保存0或者1,而是保存为 "Program" 或者 "Custom"。答案是肯定的。NHibernate提供了自定义类型的机制,通过将枚举映射为一个自定义类型,就可以自定义从数据库中读取枚举数据和保存枚举到数据库中的操作。下面详细描述一下实现方法。环境:.Net Framework 4.0,Oracle 11g R2,NHibernate 3.3.1.4000,FluentNHibernate 1.3.0.733,使用Oracle的64位ODP Oracle.DataAccess 4.112.3.0。
新增自定义类型
首先,需要新增一个自定义类型 NullableEnumCusType,它实现 IUserType 接口。这个自定义类型告诉NHibernate要把枚举保存为数据库中的 NHibernateUtil.AnsiString.SqlType 类型,以及如何从数据库中读取和保存枚举值(经由 NullSafeGet() 和 NullSafeSet() 方法)。
using System; using NHibernate; using NHibernate.SqlTypes; using NHibernate.UserTypes; namespace Zen.Framework.Data { public class EnumCusType<TEnum> : IUserType where TEnum:struct { public object Assemble(object cached, object owner) { return DeepCopy(cached); } public object DeepCopy(object value) { return value; } public object Disassemble(object value) { return DeepCopy(value); } public bool Equals(object x, object y) { return x.Equals(y); // 由于装箱后 x == y 会总是返回 false, 所以要使用 Equals() } public int GetHashCode(object x) { return x.GetHashCode(); } public bool IsMutable { get { return true; } } public object NullSafeGet(System.Data.IDataReader rs, string[] names, object owner) { return Enum.Parse(typeof(TEnum), NHibernate.NHibernateUtil.AnsiString.NullSafeGet(rs, names[0]).ToString()); } public void NullSafeSet(System.Data.IDbCommand cmd, object value, int index) { NHibernate.NHibernateUtil.AnsiString.NullSafeSet(cmd, value.ToString(), index); } public object Replace(object original, object target, object owner) { return target; } public Type ReturnedType { get { return typeof(TEnum); } } public NHibernate.SqlTypes.SqlType[] SqlTypes { get { return new SqlType[] { NHibernateUtil.AnsiString.SqlType }; } } } }