NHibernate IUserType将可为空的日期时间转换为DB not null值

NHibernate IUserType将可为空的日期时间转换为DB not null值,nhibernate,nullable,Nhibernate,Nullable,我有一个遗留数据库存储日期,这意味着没有日期为9999-21-31, 列Till_Date的类型为DateTimenotnull=“true” 在应用程序中,我要构建持久化类,该类不将任何日期表示为null, 所以我在C#//public DateTime中使用了null DateTime?TillDate{get;set;} 我创建了IUserType,它知道如何将实体空值转换为DB 9999-12-31 但NHibernate似乎没有在我的IUserType上调用SafeNullGet和Sa

我有一个遗留数据库存储日期,这意味着没有日期为9999-21-31, 列
Till_Date
的类型为DateTime
notnull=“true”

在应用程序中,我要构建持久化类,该类不将任何日期表示为null, 所以我在C#//public DateTime中使用了null DateTime?TillDate{get;set;}

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

但NHibernate似乎没有在我的IUserType上调用SafeNullGet和SafeNullSet 当实体值为null,并且报告null用于not null列时

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

如果NHibernate不支持将null值转换为NOTNULL值的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; }
        }
    }
}

问题可能出现在
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。我尝试了两个版本的Equals,关于这一点:如果比较值为null,则第一次返回true;如果比较值为null,则第二次返回false。如果比较值为null,请参阅添加的代码实现tnxI根据您的代码调整内容(在NullSafeGet中添加显式强制转换),它可以工作,但在以下情况下除外,如果在DB中,日期不是最大值(例如2010-01-01)我获取实体,将日期更改为null并保存更改,然后它调用Equals(2010-01-01,null),返回false,但不调用NullSafeSet并引发异常:NHibernate.PropertyValueException:notnull属性引用null或暂时值。然后,我在hbm.xml中将映射更改为notnull=“false”,这次调用了NullSafeSet。谢谢你的帮助,我有点惊讶NOTNULL设置会影响这一点,我认为这只是用于模式生成。如果答案有帮助,请投票并接受。结果证明问题仍然存在。但只有当您尝试在DateTime中持久化具有空值的新瞬态实体时才会发生这种情况?财产。但当更新已持久化的实体时,它工作正常。我能够找到原因,我的类映射xml具有dynamic insert=“true”。将其更改为false后,所有操作正常。(我也有dynamic update=“true”,但一切正常)在属性映射中没有null=“true”的P.S仍然失败。我需要可为null的DB-DATE类型。所以我用DateTime.MinValue替换了MaxDate,它可以正常工作