Friday, 12 November 2010

Using a Guid as a version column in NHibernate

One of the great features of NHibernate is the almost infinite control it gives you over mapping to existing database structures with its custom type mapping. Turns out custom types can also be applied to version columns for managing concurrency. Thanks to Justice for the pointer on my stackoverflow post :)

The basic method is to simply implement a NHibernate.UserTypes.IUserVersionType and implement the Seed and Next methods to return Guids. The rest is just Guid type mapping.

The custom type can then be registered in FluentNHibernate using this code:

   1:  public AnimalMap()
   2:  {                       
   3:      Id(x => x.Id);    
   4:      Map(x => x.SpeciesId);
   5:      Version(x => x.ConcurrencyId).CustomType(typeof(GuidVersionType));
   6:      Map(x => x.Name);    
   7:      Map(x => x.NumberOfLegs); 
   8:  }

The full code is below:

   1:  class GuidVersionType : NHibernate.UserTypes.IUserVersionType
   2:  {
   3:      public SqlType[] SqlTypes
   4:      {
   5:          get
   6:          {                
   7:              var types = new SqlType[1];
   8:              types[0] = new SqlType(DbType.Guid);
   9:              return types;
  10:          }
  11:      }
  12:   
  13:      public System.Type ReturnedType
  14:      {
  15:          get { return typeof(Guid); }
  16:      }
  17:   
  18:      public new bool Equals(object x, object y)
  19:      {
  20:          if (x == null)
  21:          {
  22:              return false;
  23:          }
  24:   
  25:          return x.Equals(y);
  26:      }
  27:   
  28:      public int GetHashCode(object x)
  29:      {
  30:          return x.GetHashCode();
  31:      }
  32:   
  33:      public object NullSafeGet(IDataReader rs, string[] names, object owner)
  34:      {
  35:          var value = NHibernateUtil.Guid.NullSafeGet(rs, names[0]); 
  36:          if (value == null)
  37:              return null;
  38:   
  39:          return (Guid) value;
  40:      }
  41:   
  42:      public void NullSafeSet(IDbCommand cmd, object value, int index)
  43:      {
  44:          if (value == null)
  45:          {
  46:              NHibernateUtil.Guid.NullSafeSet(cmd, null, index);
  47:              return;
  48:          }           
  49:          NHibernateUtil.Guid.NullSafeSet(cmd, value, index);
  50:      }
  51:   
  52:      public object DeepCopy(object value)
  53:      {
  54:          if (value == null) return null;
  55:          var copy = ((Guid) value);
  56:          return copy;
  57:      }
  58:   
  59:      public bool IsMutable
  60:      {
  61:          get { return false; }
  62:      }
  63:   
  64:      public object Replace(object original, object target, object owner)
  65:      {
  66:          return original;
  67:      }
  68:   
  69:      public object Assemble(object cached, object owner)
  70:      {
  71:          return cached;
  72:      }
  73:   
  74:      public object Disassemble(object value)
  75:      {
  76:          return value;
  77:      }
  78:   
  79:      public int Compare(object x, object y)
  80:      {
  81:          return ((Guid) x).CompareTo((Guid) y);
  82:      }
  83:   
  84:      public object Seed(ISessionImplementor session)
  85:      {
  86:          return Guid.NewGuid();
  87:      }
  88:   
  89:      public object Next(object current, ISessionImplementor session)
  90:      {
  91:          return Guid.NewGuid();
  92:      }
  93:  }

This works in FluentNHibernate 1.1.