博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
配置NHibernate将枚举保存为Oracle数据库中的字符串
阅读量:6647 次
发布时间:2019-06-25

本文共 7726 字,大约阅读时间需要 25 分钟。

假设有这样一个枚举:

/// /// 字典项类型/// 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
: 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 }; } } }}
EnumCusType

 

使用自定义类型

在实体的枚举属性映射时,要指定上面新增的自定义枚举类型。这样就已经可以实现枚举与数据库字符串之间的映射和转换了。

Map(t => t.ItemType, "ITEM_TYPE").CustomType
>();

 

配置可为空类型的枚举
对于可为空类型的属性,例如:

/// /// 字典项类型2/// public virtual DicItemType? ItemType2 { get; set; }

需要在增加一个针对可为空枚举的自定义类型:

using System;using NHibernate;using NHibernate.SqlTypes;using NHibernate.UserTypes;namespace Zen.Framework.Data{    public class NullableEnumCusType
: IUserType { 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) { if (x == null) { return x == y; } else { 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) { var v = NHibernate.NHibernateUtil.AnsiString.NullSafeGet(rs, names[0]); if (v == null) { return null; } else { return Enum.Parse(typeof(TEnum).GetGenericArguments()[0], v.ToString()); } } public void NullSafeSet(System.Data.IDbCommand cmd, object value, int index) { NHibernate.NHibernateUtil.AnsiString.NullSafeSet(cmd, (value == null? null : 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 }; } } }}
NullableEnumCusType

然后把可为空类型的实体属性映射为可为空的自定义枚举类型,这样可为空的枚举也没问题了。

Map(t => t.ItemType2, "ITEM_TYPE2").CustomType
>();

 

使用 EnumConvention 统一配置自定义枚举类型
之前,我们是可以在 EnumConvention 中对枚举类型进行统一配置的,也就是说,在实体映射的时候,不需要区分枚举和普通的字符型以及数字型属性,例如可以这样:

/// /// 字典类别/// public class DicItemEntityMap : InputItemMap
{ public DicItemEntityMap() { Table("SYS_DIC_ITEM"); Id(t => t.Id, "DIC_ITEM_ID"); Map(t => t.IsStop, "IS_STOP"); Map(t => t.ItemType, "ITEM_TYPE"); Map(t => t.ItemType2, "ITEM_TYPE2"); References(t => t.Category, "DIC_CATEGORY_ID"); }}

这是因为 FluentNHibernate 提供了 Convention  机制可以对各种类型进行统一配置。例如只要实现这样一个 EnumConvention 就可以对所有枚举统一配置:

public class EnumConvention : IUserTypeConvention{    public void Accept(FluentNHibernate.Conventions.AcceptanceCriteria.IAcceptanceCriteria
criteria) { // 匹配枚举或可为空枚举 criteria.Expect(x => x.Property.PropertyType.IsEnum || (x.Property.PropertyType.Name == "Nullable`1" && x.Property.PropertyType.GetGenericArguments().Length > 0 && x.Property.PropertyType.GetGenericArguments()[0].IsEnum)); } public void Apply(FluentNHibernate.Conventions.Instances.IPropertyInstance instance) { instance.CustomType(instance.Property.PropertyType); }}
EnumConvention

能不能把上面的 EnumConvention 改造一下,适配新增的 EnumCusType 和 NullableEnumCusType 呢?这样就不用每次都写 ” Map(t => t.ItemType, "ITEM_TYPE").CustomType<EnumCusType<DicItemType>>() “ 这样烦人的配置了。

但是你马上就会发现 EnumCusType 是非常坑爹的泛型,在 EnumConvention 里没法直接调用 instance.CustomType() 进行配置。好在微软提供了非常强大的Emit,动态生成一个泛型类型非常轻松:

public class EnumConvention : IUserTypeConvention{    public void Accept(FluentNHibernate.Conventions.AcceptanceCriteria.IAcceptanceCriteria
criteria) { // 匹配枚举或可为空枚举 criteria.Expect(x => x.Property.PropertyType.IsEnum || (x.Property.PropertyType.Name == "Nullable`1" && x.Property.PropertyType.GetGenericArguments().Length > 0 && x.Property.PropertyType.GetGenericArguments()[0].IsEnum)); } public void Apply(FluentNHibernate.Conventions.Instances.IPropertyInstance instance) { //instance.CustomType(instance.Property.PropertyType); if (instance.Property.PropertyType.Name == "Nullable`1") { // 转换为可空枚举自定义类型 Type t = TypeBuilder.GetType( string.Format("Zen.Framework.Data.NullableEnumCusType`1[[{0}]]", instance.Property.PropertyType.AssemblyQualifiedName)); instance.CustomType(t); } else { // 转换为非可空枚举自定义类型 Type t = TypeBuilder.GetType( string.Format("Zen.Framework.Data.EnumCusType`1[[{0}]]", instance.Property.PropertyType.AssemblyQualifiedName)); instance.CustomType(t); } }}
使用Emit配置泛型自定义类型

 

转载地址:http://tvuto.baihongyu.com/

你可能感兴趣的文章
ssh服务器配置方法
查看>>
Django安装配置
查看>>
bootstrap源码学习与示例:bootstrap-tab
查看>>
[C] 让VC支持C99的整数类型V1.01。避免包含目录问题,更名auto_stdint.h、auto_inttypes.h(在VC6至VC2012、GCC、BCB等编译器下测试通过)...
查看>>
Apache OFBiz 10.04.05 发布,安全漏洞修复
查看>>
京东书4
查看>>
Java EE之RMI
查看>>
ASCII编码表 -- SQL注入 也需要
查看>>
MySQL常用数据类型介绍
查看>>
当装系统时遇到“选中的磁盘采用GPT分区形式”
查看>>
CentOS的远程桌面(xdm)
查看>>
Some aspects to prepare
查看>>
oracle 好书 05 ( 内存组件与 oracle 进程 )
查看>>
Linux gdb符号调试器
查看>>
http_load
查看>>
metasploit tutorial
查看>>
安装 iis 的方式
查看>>
Spring MVC 拦截器问题,如何配置不需要拦截的页面
查看>>
覆盖距离AsiaHatyai-2012 & LA 6144 - Radiation 二分搜索
查看>>
LINQ : IEnumerable<T> and IQueryable<T>区别
查看>>