For every property on the entity that you map, NHibernate uses its own defaults to determine the right database column type. On most occasions, this just works. If it does not and you need minor changes to type, for example, instead of Int32
you want Int64
, you can do it via entity mapping. But if you need to map a database column to a property of a completely unrelated type, then you can define your own type for NHibernate to work with. This obviously assumes that your code knows both the type in the database and the type in the code. Your code also should know how these two types convert from each other.
Consider for example, you are working on a legacy database situation. You have got an Employee
table which has a NVARCHAR(1)
type of column named IsEmployed
. A value of Y
in this column means that the employee is still part of the organization. A value of N
in this column means that the employee has left the organization. Now, while building the domain model for this domain, you may not want to use a string type to designate whether the employee is still part of the organization or not. You would rather use a Boolean. This conversion from a string type to Boolean is something NHibernate cannot do out of the box and we would need to define a type that can do the conversion for us. Let's see how we can handle this situation using a user-defined type.
Situations like this, where you use a Boolean type on the application side and best suitable type on the database side, can also be handled using query substitutions. Query substitutions would let you use Boolean types in a database independent way. Readers are advised to explore this option before making the final choice.
A user-defined type is any class that implements the NHibernate.UserTypes.IUserType
interface. Once this class is defined, we can use it during mapping of a property to tell NHibernate that it should use this class whenever it is dealing with that particular property. Let's see how this works by implementing a type to handle conversion from string to Boolean type we just discussed. Following code listing shows a class YesNoType
that implements IUserType
:
public class YesNoType : IUserType { public bool Equals(object x, object y) { if (ReferenceEquals(x, y)) { return true; } if (x == null || y == null) { return true; } return x.Equals(y); } public int GetHashCode(object x) { return x.GetHashCode(); } public object NullSafeGet(IDataReader rs, string[] names, object owner) { var value = rs[names[0]] as string; if (value == @"Y") return true; return false; } public void NullSafeSet(IDbCommand cmd, object value, int index) { var parameter = (DbParameter)cmd.Parameters[index]; if (value == null) { parameter.Value = DBNull.Value; } else { var bValue = (bool) value; if (bValue) { parameter.Value = "Y"; } else { parameter.Value = "N"; } } } 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[] { new SqlType(DbType.String), }; } } public Type ReturnedType { get { return typeof (bool); } } public bool IsMutable { get { return false; } } }
There is a lot of code in the above implementation. But on careful observation, you would realize that some of the methods are not new and are clearly needed for NHibernate to do its job.
Let's go over the important methods to understand their role in the process:
Let's tell NHibernate to use this type. If I had a Boolean property named IsEmployed
on the Employee
entity, then following is how I would map it to make use of YesNoType
:
public class EmployeeMappings : ClassMapping<Employee> { public EmployeeMappings() { Property(e => e.IsEmployeed, mapper => mapper.Type<YesNoType>()); } }
Now you can use the IsEmployed
property on the Employee
entity as a normal Boolean property. Every time the property value needs to be updated into the database or needs to be read from the database, NHibernate would handle the conversion.
In the beginning, when I said that NHibernate does not support the preceding conversion out-of-the-box, I lied. In reality, the example we used is so common that NHibernate has a built-in type to support this. It is called NHibernate.Type.YesNoType
. The built-in type is not implemented as a user-defined type but does exactly the same thing. If you come across a similar situation then you can avoid implementing a user defined type by making use of this build-in type. There are several such built-types to support commonly occurring legacy database situations.
IUserType
is the most basic form of a user defined type. NHibernate has defined interfaces to represent more specific user defined types. These types can be used instead of IUserType
if the latter cannot handle the situation sufficiently. Following table describes these additional user defined types:
Examples or detailed explanation of each of these types is beyond the scope of this book. If you ever need to use one of the above types, feel free to ask for a helping hand on NHibernate user group.