# Saturday, December 30, 2006

Often times when you're developing an application, there is a one-to-one mapping between your domain model (object model) and your database schema.  Doing it this way often times makes it easier to wrap your head around everything going on in your app.

 

But this isn’t always the right way to do things.  For example, take the highlighted columns in the UserCredential table:


 

In the UserCredential class, do you really want to have HashedPassword, HashType, and PasswordSalt properties?  Probably not...  So, how can we still do our mapping with NHibernate and avoid creating a clunky UserCredential class?  Enter NHibernates CompositeUserTypes!

 

First, let’s create our domain objects:

Notice that the UserCredential class contains a property called Password, which maps to the class Password.

 

Now, let’s create our CompositeUserType, in our case, we’ll call it PasswordCompositeUserType, and this class will implement the NHibernate.IUserType interface:

public class PasswordCompositeUserType : IUserType { public new bool Equals(object x, object y) { if (x == y) return true; if (x == null || y == null) return false; return x.Equals(y); } public object DeepCopy(object value) { if (value == null) return null; else return ((Password)value).Copy(); } public int GetHashCode(object x) { return x.GetHashCode(); } public bool IsMutable { get { return false; } } public object NullSafeGet(System.Data.IDataReader rs, string[] names, object owner) { if (rs.IsDBNull(rs.GetOrdinal(names[0])) || rs.GetOrdinal(names[0]) == string.Empty) return null; string hashedPassword = (string)rs[names[0]]; long salt = (long)rs[names[1]]; HashType hashType = (HashType)Enum.Parse(typeof(HashType), (string)rs[names[2]]); Password result = new Password(); result.HashedPassword = hashedPassword; result.Salt = salt; result.HashType = (HashType)Enum.Parse(typeof(HashType), hashType); return result; } public void NullSafeSet(System.Data.IDbCommand cmd, object value, int index) { if (value == null) { ((IDataParameter)cmd.Parameters[index]).Value = null; ((IDataParameter)cmd.Parameters[index + 1]).Value = null; ((IDataParameter)cmd.Parameters[index + 2]).Value = null; } else { Password pass = (Password)value; ((IDataParameter)cmd.Parameters[index]).Value = pass.HashedPassword; ((IDataParameter)cmd.Parameters[index + 1]).Value = pass.Salt; ((IDataParameter)cmd.Parameters[index + 2]).Value = pass.HashType; } } public Type ReturnedType { get { return typeof(Password); } } public global::NHibernate.SqlTypes.SqlType[] SqlTypes { get { global::NHibernate.SqlTypes.SqlType[] types = new global::NHibernate.SqlTypes.SqlType[3]; types[0] = new global::NHibernate.SqlTypes.SqlType(DbType.String); types[1] = new global::NHibernate.SqlTypes.SqlType(DbType.Int64); types[2] = new global::NHibernate.SqlTypes.SqlType(DbType.String); return types; } } }

Looks complicated, but it’s not.  Let’s break it down method by method.

public new bool Equals(object x, object y) – Returns whether object x and y are equal.  Why does it matter?  In our case, it really doesn’t (that I’m aware of); but NHibernate uses this to figure out the relationship between objects in your domain model.

public object DeepCopy(object value) – Creates a deep copy, I’m not sure why NHibernate needs this.  Note, in my implimentation of Password, I created a Copy method.  You could also create a new Password object, set the properties of that new object, and return that as well.  Something like this:

Password p = (Password)value;

Password result = new Password();

result.HashedPassword = p.HashedPassword;

result.HashType = p.HashType;

result.Salt = p.Salt;

return result;

 

I don’t know about you, but I prefer to encapsulate all this into the object itself :).

public int GetHashCode(object x) - Returns the hashcode for the object.

public bool IsMutable – Is this object mutable?  In our case, it doesn’t matter so we return false.

public object NullSafeGet(System.Data.IDataReader rs, string[] names, object owner) – This is where a lot of the magic happens :) First thing to do is check if the value from the database is null or empty, if it is, then we can return null and be done.  Second step, grab all the values from the data reader.

This is one of the things I don’t like about NHibernate, you have to get the values out by ordinal value.  Not only by the ordinal value, by in the same order they are mapped in the NHibernate mapping file (more on this later).  And finally, build and return a Password object.

public void NullSafeSet(System.Data.IDbCommand cmd, object value, int index) – This is the other half of the magic :) This code is fairly straight forward so I won’t bother to explain it.

public Type ReturnedType – Tells NHibernate what type of object will be returned by this CompositeUserType.

public global::NHibernate.SqlTypes.SqlType[] SqlTypes – Tells NHibernate what data types of each column is.  Again, the the columns are in ordinal order.  Why “the global::” you might ask?  Well, we have a class called NHibernate in our DataAccessLayer, so the global:: bit tells the C# compiler to backup and look in the global NHibernate namespace.  If you don’t have this issue, you can get rid of the global:: part.

 

Now, onto the Hibernate mapping file:

<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"> <class name="MyNamespace.DomainModel.UserCredential, MyNamespace.DomainModel" table="UserCredential" lazy="false"> <id name="Id" column="ID" type="Guid"> <generator class="guid.comb"/> </id> ... <property name="Password" type="MyNamespace.DataAccess.PasswordCompositeUserType, MyNamespace.DataAccess" > <column name="HashedPassword"/> <column name="Salt"/> <column name="HashType"/> </property> <property name="IsActive" /> </class> </hibernate-mapping>

Notice that the column order in the mapping file for our CompositeUserType in our mapping file is in the exact same order as in our PasswordCompositeUserType class.

 

I know this post was very long, but hopefully you found it useful and can adapt it to meet your specific needs!  Next up, using a NHibernate UserType to map the .NET IPAddress class to/from your database.


Questions, comments, please feel free to leave a comment.  This is my first article and I'd like as much feedback as possible!

Sunday, January 21, 2007 12:07:39 AM (Alaskan Standard Time, UTC-09:00)
christian crowhurst
Sunday, January 21, 2007 10:33:01 AM (Alaskan Standard Time, UTC-09:00)
Christian, Thank you for your comment! Oran Dennison (Generic NHibernate Enum String Mapping) is actually one of my co-workers; and he and I worked together to create the generic enum mapper.
Dan morphis
All comments require the approval of the site owner before being displayed.
Name
E-mail
(will show your gravatar icon)
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):

Live Comment Preview