Oracle 托管驱动程序 ODP.NET 与 NHibernate 4.0 FLOAT (126) 到 C# DECIMAL/Double

Oracle managed driver ODP.NET with NHibernate 4.0 FLOAT (126) to C# DECIMAL/Double

我正在尝试使用 Fluent NHibernate 和 Oracle 11g 创建一个映射。这是我正在创建的映射:-

public class Abc : ClassMap<Abc>
{
    public Abc() 
    {
        Table("Abc");
        DynamicUpdate();

        Id(x => x.Id, "IdColumn").GeneratedBy.GuidComb();
        Map(x => x.DecimalColumn, "DecimalColumn").Formula("TRUNC(DecimalColumn, 28)");
    }
}

现在,当我使用条件查询获取数据时:-

var criteria = Session.QueryOver<Abc>().Where(x => x.Id == Id);
criteria.Future();

对于小数点超过 28 位的列,它会抛出 Nhibernate.Exceptions.GenericADOException 和 InnerException (InvalidCastException),因此它需要 t运行cation。

但是如果我从映射中删除该列(注意 DecimalColumn 缺少列名称字符串),它会起作用。

public class Abc : ClassMap<Abc>
    {
        public Abc() 
        {
            Table("Abc");
            DynamicUpdate();

            Id(x => x.Id, "IdColumn").GeneratedBy.GuidComb();
            Map(x => x.DecimalColumn).Formula("TRUNC(DecimalColumn, 28)");
        }
    }

问题是我使用 SQLite 进行了测试,而 SQLite 不喜欢省略列名。

因此,如果我通过删除列名来修复它,那么我就无法 运行 测试。有什么想法吗?

所以,我想通了如下:-

  1. 数据库列是 float(126),它比 .NET 支持的精度更高,因此,在某些情况下,当 .NET 无法处理数据(溢出)时会抛出异常。 (Oracle number to C# decimal)
  2. 当在 fluent mapping 中指定公式时,列变为只读。在上面指定的第二种情况下可以读取数据的原因是正在使用公式,但这也意味着不能将相同的映射用于 insert/Update DecimalColumn。 (How to map an NHibernate entity property using both a formula and a column specification)

如上所述,类型是数据库 (FLOAT 126),它可以比 .NET 支持的精度(位数)更高。理想的解决方案是更改 FLOAT(53) 的数据库列类型或更小的数据,如果数据永远不会需要超过 FLOAT(53) (精度大约 15 位数字),但如果这不可能,则可以执行以下操作.

因此可以在不将字段设置为只读(使用 NHibernate 映射中的公式)的情况下解决溢出问题。可以使用自定义用户类型。在用户类型的 "NullSafeGet" 方法中使用 DataReader.GetDouble 从数据 reader 中读取数据并且它有效。

我正在分享下面的代码(绝对可以改进,不应该在没有理解和改进它的情况下用于生产,因为我不确定所有方法是否正确实施)。

    namespace ABCD
    {
        using System;
        using System.Data;

        using NHibernate;
        using NHibernate.SqlTypes;
        using NHibernate.UserTypes;

        public class MyDecimalType : IUserType 
        {
            public bool IsMutable
            {
                get
                {
                    return false;
                }
            }

            public System.Type ReturnedType
            {
                get
                {
                    return typeof(decimal);
                }
            }

            public NHibernate.SqlTypes.SqlType[] SqlTypes
            {
                get
                {
                    return new[] { SqlTypeFactory.GetSqlType(DbType.Decimal) };
                }
            }

            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);
            }

            bool IUserType.Equals(object x, object y)
            {
                if (object.ReferenceEquals(x, y))
                {
                    return true;
                }

                if (x == null || y == null)
                {
                    return false;
                }

                return x.Equals(y);
            }

            public int GetHashCode(object x)
            {
                return x.GetHashCode();
            }

            public object NullSafeGet(System.Data.IDataReader rs, string[] names, object owner)
            {
                var index = rs.GetOrdinal(names[0]);
                var obj = rs.GetDouble(index);
                return Convert.ToDecimal(obj);
            }

            public void NullSafeSet(System.Data.IDbCommand cmd, object value, int index)
            {
                if (value == null)
                {
                    ((IDataParameter)cmd.Parameters[index]).Value = DBNull.Value;
                }
                else
                {
                    ((IDataParameter)cmd.Parameters[index]).Value = value;
                }
            }

            public object Replace(object original, object target, object owner)
            {
                return original;
            }
        }
}

然后在映射中使用自定义用户类型:-

    public class Abc : ClassMap<Abc>
    {
        public Abc() 
        {
            Table("Abc");
            DynamicUpdate();

            Id(x => x.Id, "IdColumn").GeneratedBy.GuidComb();
            Map(x => x.DecimalColumn, "DecimalColumn").CustomType<MyDecimalType>();
        }
    }