I was a little bit upset when I discovered that persisting a DateTime object with NHibernate in SQL Server 2005 only stored values to second precision. This was unexpected as I thought that I would not lose that much (any?) information from my DateTime instance.
So I went to the internet with problem in hand and tried to find a nice little solution for me. It didn't happen definitively so I though I might write about my solution here. I think that may be what blogs are for.
Implementing NHibernate.UserTypes.IUserType
A custom user type seemed to be the best option so I politely introduced myself to IUserType. Implementing this interface enables NHibernate to perform custom persistence operations at my specification and I'll be damned if it isn't empowering to do so!
The code below has been snipped for brevity but the full implementation is available at the end of this article. Here are the juicy bits ....
public class PreciseDateTimeUserType : IUserType
{
#region IUserType Members
......
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
int ordinal = rs.GetOrdinal(names[0]);
if (rs.IsDBNull(ordinal))
{
return new NullPreciseDateTime();
}
else
{
long value = rs.GetInt64(ordinal);
return new PreciseDateTime(value);
}
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
long val = ((PreciseDateTime) value);
((IDbDataParameter) cmd.Parameters[index]).Value = val;
}
.....
#endregion
}
My PreciseDateTime class
The code above introduces PreciseDateTime - a class I have created to support this process. This class represents a DateTime by internally storing it as an Int64 in FileTimeUTC format. This is the key to providing the increased precision (just be aware that the values become unreadable in the database). The PreciseDateTime class looks like this ...
public class PreciseDateTime
{
protected static long NullValue = long.MinValue;
private long _dateTimeStorage;
public PreciseDateTime(long dateTimeLong)
{
_dateTimeStorage = dateTimeLong;
}
public bool IsNull
{
get { return _dateTimeStorage == NullValue; }
}
public PreciseDateTime Clone()
{
return new PreciseDateTime(this);
}
public static implicit operator long(PreciseDateTime preciseDateTime)
{
if (preciseDateTime.IsNull)
{
return NullValue;
}
else
{
return preciseDateTime._dateTimeStorage;
}
}
public static implicit operator DateTime?(PreciseDateTime preciseDateTime)
{
if (preciseDateTime.IsNull)
{
return null;
}
else
{
return
DateTime.FromFileTimeUtc(preciseDateTime._dateTimeStorage);
}
}
public static implicit operator PreciseDateTime(DateTime? dateTime)
{
return
new PreciseDateTime((dateTime.HasValue)
? dateTime.Value.ToFileTimeUtc()
: NullValue);
}
}
Notice how cool implicit operators are in this instance. Basically my code takes any DateTime? representation and implicitly converts it to a PreciseDateTime object without my intervention. This makes for more readable code and prevents errors that could be introduced by manual conversions. Use it like this ...
MyObject.PreciseDateTime = DateTime.Now;
Use with Castle ActiveRecord
To start using this implementation in Castle ActiveRecord you can simply do this (inserting your own namespace where appropriate) ...
private PreciseDateTime _created;
[Property(ColumnType = "Project.Domain.UserTypes.PreciseDateTimeUserType, Project.Domain")]
public virtual PreciseDateTime Created
{
get { return _created; }
set { _created = value; }
}
So I am now assured of not forgetting (again) how to implement custom user types for NHibernate and Castle ActiveRecord. If anything here is blatantly wrong please shout out - for everyone's sake.
If you crave more information about FileTimeUtc and what it represents go to the almighty MSDN
Full code for this article is available here :
PreciseDateTimeUserType.cs (3.71 kb)