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)
```