NHibernate IUserType not being applied on Insert
I'm using NHibernate with Postgresql as the backend and had to create custom types for converting System.DateTime to Postgresql "time" types as well as System.TimeSpans to "interval" db types. The IUserType I created are working and are being applied for reads and updates but they are never being applied when I try to insert an object into the database. I've set breakpoints in the IUserTypes but they never are hit when an insert is happening.
I think the issue may be around the fact that the object is just a POCO object and isn't a proxy yet so the mapping that applies the transformation to the usertype doesn't happen when I set the value of my property.
If I do an update and set开发者_开发百科 one of these properties I can see the breakpoints in the IUserType are fired.
Any thoughts?
UPDATE The Equals Method of the IUserType is being called but the NullSafeSet where I do the necessary conversion is not.
EDIT Added code samples
public class TimeType : BaseImmutableUserType<TimeSpan>
{
public override object NullSafeGet(IDataReader rs, string[] names, object owner)
{
var val = NHibernateUtil.Time.NullSafeGet(rs, names[0]);
if (val == null)
return null;
var dt = DateTime.Parse(val.ToString());
return new TimeSpan(0, dt.Hour, dt.Minute, dt.Second);
}
public override void NullSafeSet(IDbCommand cmd, object value, int index)
{
var obj = (TimeSpan)value;
((IDbDataParameter) cmd.Parameters[index]).Value = obj;
}
public override SqlType[] SqlTypes
{
get
{
return new[] { SqlTypeFactory.Time };
}
}
}
public abstract class BaseImmutableUserType<T> : IUserType
{
public abstract object NullSafeGet(IDataReader rs, string[] names, object owner);
public abstract void NullSafeSet(IDbCommand cmd, object value, int index);
public abstract SqlType[] SqlTypes { get; }
public BaseImmutableUserType()
{
int i = 0;
}
public new bool Equals(object x, object y)
{
if (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 DeepCopy(object value)
{
return value;
}
public object Replace(object original, object target, object owner)
{
return original;
}
public object Assemble(object cached, object owner)
{
return DeepCopy(cached);
}
public object Disassemble(object value)
{
return DeepCopy(value);
}
public Type ReturnedType
{
get { return typeof(T); }
}
public bool IsMutable
{
get { return false; }
}
}
I found the following two options:
Option 1: do not use your own UserType. Instead use NHibernate's own TimeAsTimeSpan like this:
Map(x => x.TimeFrom)
.CustomType("TimeAsTimeSpan");
(Example taken from here)
Option 2: Modify your class a little:
public class TimeType : BaseImmutableUserType<TimeSpan>
{
// this is taken from the source of NHibernate.Type.TimeAsTimeSpanType
private static readonly DateTime BaseDateValue = new DateTime(1753, 01, 01);
public override object NullSafeGet(IDataReader rs, string[] names, object owner)
{
var val = NHibernateUtil.TimeAsTimeSpan.NullSafeGet(rs, names[0]);
if (val == null)
return null;
var dt = DateTime.Parse(val.ToString());
return new TimeSpan(0, dt.Hour, dt.Minute, dt.Second);
}
public override void NullSafeSet(IDbCommand cmd, object value, int index)
{
//var obj = (TimeSpan)value; // we can't use TimeSpan here but need to use DateTime
// this is taken from the source of NHibernate.Type.TimeAsTimeSpanType
DateTime date = BaseDateValue.AddTicks(((TimeSpan)value).Ticks);
((IDbDataParameter)cmd.Parameters[index]).Value = date;
}
public override SqlType[] SqlTypes
{
get
{
return new[] { NHibernate.SqlTypes.SqlTypeFactory.Time };
}
}
}
精彩评论