NHibernate usertype 'Date' and LINQ

Let's say you want a property to represent a date. You could use DateTime type, but you would have to format the value to get rid of the time part. Instead, you could create a Date type. ``` csharp public struct Date { public int Year { get; set; } public int Month { get; set; } public int Day { get; set; } } ``` Now you want to use it with NHibernate. One option is to make it a component and span those properties to multiple columns. The other options is to use usertype (and use only one date/datetime column in db). Below is the UserType definition ``` csharp public class DateUserType : SimpleUserTypeBase { public override object NullSafeGet(System.Data.IDataReader rs, string[] names, object owner) { var value = NHibernateUtil.DateTime.NullSafeGet(rs,names[0]) as DateTime?; if (value == null) { return new Date(); } return new Date { Year = value.Value.Year, Month = value.Value.Month, Day = value.Value.Day }; } public override void NullSafeSet(System.Data.IDbCommand cmd, object value, int index) { if (value == null) { NHibernateUtil.DateTime.NullSafeSet(cmd, null, index); } else { var date = (Date)value; var datetime = new DateTime(date.Year, date.Month, date.Day); NHibernateUtil.DateTime.NullSafeSet(cmd, datetime, index); } } public override SqlType[] SqlTypes { get { return new[] { new SqlType(System.Data.DbType.DateTime) }; } } } ``` and the SimpleUserTypeBase base class is here (I haven't used UserTypes much, so I'm not sure if this is the best way to implement them) ``` csharp public abstract class SimpleUserTypeBase : IUserType { 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); } 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 bool IsMutable { get { return false; } } public object Replace(object original, object target, object owner) { return original; } public Type ReturnedType { get { return typeof(T); } } public abstract object NullSafeGet(System.Data.IDataReader rs, string[] names, object owner); public abstract void NullSafeSet(System.Data.IDbCommand cmd, object value, int index); public abstract SqlType[] SqlTypes { get; } } ``` With Fluent NHibernate you assign UserType to a property like this ``` csharp Map(x => x.DateOfBirth).CustomType(); ```

LINQ

To make Date type available to LINQ you have to create a HqlGenerator. I took a look at NHibernate's DateTimeHqlGenerator and came up with this ``` csharp public class DateHqlGenerator : BaseHqlGeneratorForProperty { public DateHqlGenerator() { SupportedProperties = new [] { ReflectionHelper.GetProperty((Date x) => x.Year), ReflectionHelper.GetProperty((Date x) => x.Month), ReflectionHelper.GetProperty((Date x) => x.Day), }; } public override NHibernate.Hql.Ast.HqlTreeNode BuildHql(MemberInfo member, Expression expression, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor) { return treeBuilder.MethodCall(member.Name.ToLowerInvariant(), visitor.Visit(expression).AsExpression()); } } ``` Then you have to create a class that registers generators ``` csharp public class LinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry { public LinqToHqlGeneratorsRegistry() { this.Merge(new DateHqlGenerator()); } } ``` Last step is to set NHibernate property 'linqtohql.generatorsregistry' ``` csharp nhConfig.Properties.Add("linqtohql.generatorsregistry", typeof(LinqToHqlGeneratorsRegistry).AssemblyQualifiedName); ``` And now you can make queries like ``` csharp session.Query().Where(p => p.DateOfBirth.Month == 10) ```