【问题标题】:NHibernate IUserType convert nullable DateTime to DB not-null valueNHibernate IUserType 将可为空的 DateTime 转换为 DB 非空值
【发布时间】:2011-01-31 14:55:36
【问题描述】:

我有存储日期的旧数据库,这意味着无日期为 9999-21-31, Till_Date 列的类型为 DateTime not-null="true"

在应用程序中,我想构建将无日期表示为 null 的持久类, 所以我在 C#//public DateTime 中使用了可为空的 DateTime?截止日期 {get;放; }

我创建了知道将实体空值转换为 DB 9999-12-31 的 IUserType

但似乎 NHibernate 没有在我的 IUserType 上调用 SafeNullGet、SafeNullSet 当实体值为空时,报告一个空用于非空列。

我试图通过将列映射为 not-null="false" 来绕过它 (仅更改了映射文件,未更改数据库) 但它仍然没有帮助,只是现在它尝试将空值插入数据库并获取 ADOException。

是否知道 NHibernate 不支持将 null 转换为非 null 值的 IUseType?

//在需要修复之前实施!!!

public class NullableDateTimeToNotNullUserType : IUserType
{
        private static readonly DateTime MaxDate = new DateTime(9999, 12, 31);

        public new bool Equals(object x, object y)
        {                                                   //This didn't work as well
            if (ReferenceEquals(x, y)) return true; //if(x == null && y == null) return false;

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

            return x.Equals(y);
        }

        public int GetHashCode(object x)
        {
            return x == null ? 0 : x.GetHashCode();
        }

        public object NullSafeGet(IDataReader rs, string[] names, object owner)
        {
            var value = rs.GetDateTime(rs.GetOrdinal(names[0]));

            return (value == MaxDate)? null : value;
        }

        public void NullSafeSet(IDbCommand cmd, object value, int index)
        {
            var dateValue = (DateTime?)value;

            var dbValue = (dateValue.HasValue) ? dateValue.Value : MaxDate;

            ((IDataParameter)cmd.Parameters[index]).Value = dbValue;
        }

        public object DeepCopy(object value)
        {
            return value;
        }

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

        public object Assemble(object cached, object owner)
        {
            return cached;
        }

        public object Disassemble(object value)
        {
            return value;
        }

        public SqlType[] SqlTypes
        {
            get { return new[] { NHibernateUtil.DateTime.SqlType }; }
        }

        public Type ReturnedType
        {
            get { return typeof(DateTime?); }
        }

        public bool IsMutable
        {
            get { return false; }
        }
    }
}

//带有修复的最终实现。

//Make the column mapping in hbm.xml not-null="false" even if in DB null not allowed.
//Make sure the class mapping in xml doesn't have dynamic-insert="true"

public class NullableDateTimeToNotNullUserType : IUserType
{
        private static readonly DateTime MaxDate = new DateTime(9999, 12, 31);

        public new bool Equals(object x, object y)
        {                                                   //This didn't work as well
            if (ReferenceEquals(x, y)) return true; //if(x == null && y == null) return false;

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

            return x.Equals(y);
        }

        public int GetHashCode(object x)
        {
            return x == null ? 0 : x.GetHashCode();
        }

        public object NullSafeGet(IDataReader rs, string[] names, object owner)
        {
            var value = NHibernateUtil.Date.NullSafeGet(rs, names[0]);

            return (value == MaxDate)? default(DateTime?) : value;
        }

        public void NullSafeSet(IDbCommand cmd, object value, int index)
        {
            var dateValue = (DateTime?)value;

            var dbValue = (dateValue.HasValue) ? dateValue.Value : MaxDate;

            NHibernateUtil.Date.NullSafeSet(cmd, valueToSet, index);
        }

        public object DeepCopy(object value)
        {
            return value;
        }

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

        public object Assemble(object cached, object owner)
        {
            return cached;
        }

        public object Disassemble(object value)
        {
            return value;
        }

        public SqlType[] SqlTypes
        {
            get { return new[] { NHibernateUtil.DateTime.SqlType }; }
        }

        public Type ReturnedType
        {
            get { return typeof(DateTime?); }
        }

        public bool IsMutable
        {
            get { return false; }
        }
    }
}

【问题讨论】:

    标签: nhibernate nullable


    【解决方案1】:

    问题可能出在您对Equals 的实现中。 NHibernate 使用 Equals 来确定是否需要获取或设置值。请发布您的 IUserType 实现。

    编辑:好的,我想我看到了问题。试试这个:

    public object NullSafeGet(IDataReader rs, string[] names, object owner)
    {
        var value = NHibernateUtil.Date.NullSafeGet(rs, names[0]);
        return (value == MaxDate) ? null : value;
    }
    
    public void NullSafeSet(IDbCommand cmd, object value, int index)
    {
        var dateValue = (DateTime?)value;
        var dbValue = (dateValue.HasValue) ? dateValue.Value : MaxDate;
        NHibernateUtil.Date.NullSafeSet(cmd, valueToSet, index);
    }
    

    NullSafeGet 可能还需要将返回值转换为 DateTime?。您对 Equals 的实现看起来是正确的。

    【讨论】:

    • 使用调试我看到对equals的调用为两个比较值传递了null。我尝试了 2 个版本的 Equals,关于这一点:如果比较值为 null,则第一个返回 true;如果比较值为 null,则第二个返回 false,请参阅添加的代码实现 tnx
    • 我根据您的代码调整了一些东西(在 NullSafeGet 中添加了显式转换)它可以工作,除非在以下情况下,如果在 DB 中日期不是最大值(例如 2010-01-01)并且我获取实体,将日期更改为 null 并保存更改,然后调用返回 false 的 Equals(2010-01-01, null),但不调用 NullSafeSet 并抛出异常:NHibernate.PropertyValueException: not-null property references空值或瞬态值。然后我在 hbm.xml 中将映射更改为 not-null="false",并且这次调用 NullSafeSet 时它起作用了。感谢您的帮助
    • 我有点惊讶于非空设置会影响这一点,我认为这仅用于模式生成。如果有帮助,请点赞并接受答案。
    • 原来问题依然存在。但仅当您尝试在 DateTime 中保留具有空值的新瞬态实体时才会发生?财产。不过,在更新已经持久化的实体时它可以正常工作。我能够找到导致它的原因,我的类映射 xml 具有动态插入 =“真”。将其更改为 false 后一切正常。 (我也有 dynamic-update="true" 但一切正常) P.S 在属性映射中 not-null="true" 仍然失败。
    • 我需要可为空的 DB-DATE 类型。所以我只是用 DateTime.MinValue 替换了 MaxDate 并且它可以工作
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-21
    • 1970-01-01
    • 2014-10-20
    • 2016-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多