Search code examples
c#nhibernatesessionguidhbmxml

Making NHibernate "translate" C# POCO Guid property with value of System.Guid.Empty to be saved as NULL for the Database uniqueidentifier field?


Our office team is working and ASP.NET project that uses .NET Framework 4 and NHibernate 3.3.1

I have a POCO domain class called AllResourcesInfo ( which ultimately maps to a Resource table in the database that is inner joined to some other tables, but just to keep things simple, let's just say that the AllResourcesInfo maps to the Resource table ).

I also have a POCO domain class called Department ( which ultimately maps to a Department table in the database )

The AllResourcesInfo could possibly have a many-to-one relationship with a Department.

If an instance of AllResourcesInfo belongs to a department then the AllResourcesInfo.DepartmentDatabaseID has a valid DepartmentDatabaseID that is in the Department entity.

However, if the instance of AllResourcesInfo does Not belong to a department then the AllResourcesInfo.DepartmentDatabaseID has a NULL value in the datbase. database.

When I use NHibernate to retrieve( basically NHibernate does a read on the Database ) values from AllResourcesInfo table, NHibernate will populate the C# POCO property of AllResourcesInfo.DepartmentDatabaseID with the 00000000-0000-0000-0000-000000000000 value ( ie the System.Guid.Empty value ) if the AllResourcesInfo does Not belong to a department

However, when I use NHibernate Session to save a brand new C# POCO instance of AllResourcesInfo which does Not belong to any department , I populate the POCO property of AllResourcesInfo.DepartmentDatabaseID with the 00000000-0000-0000-0000-000000000000 value ( ie the System.Guid.Empty value ) But The Database will Obviously Throw an Error Because a Guid value of 00000000-0000-0000-0000-000000000000 obviously fails to exist in Department table of the Database.

To summarize, NHibernate Session Save fails to save(or leave ) a uniqueidentifier field in the Database with a value of NULL when we want it to leave a uniqueidentifier field value as null.

Could someone please tell me how we can ensure that NHibernate "translates" a C# POCO Guid property with a value of System.Guid.Empty to be saved( or left) as NULL for the Database uniqueidentifier field value?


Solution

  • You need to implement NHibernate's IUserType.

    It should like this:

    using NHibernate.SqlTypes;
    using NHibernate.UserTypes;
    
    [Serializable] //to allow NH configuration serialization
    public class GuidTypeConverter : IUserType
    {
        SqlType[] sqlTypes;
    
        public GuidTypeConverter()
        {
            sqlTypes = new[] { SqlTypeFactory.GetSqlType(DbType.Guid, 0, 0) };
        }
    
        public SqlType[] SqlTypes
        {
            get { return sqlTypes; }
        }
    
        public Type ReturnedType
        {
            get { return typeof(Guid); }
        }
    
        public object NullSafeGet(IDataReader rs, string[] names, object owner)
        {
            if (rs[names[0]] == DBNull.Value)
            {
                return Guid.Empty;
            }
    
            return (Guid) rs[names[0]];
        }
    
        public void NullSafeSet(IDbCommand cmd, object value, int index)
        {
            var param = (IDataParameter) cmd.Parameters[index];
            param.DbType = sqlTypes[0].DbType;
            var guid = (Guid) value;
    
            if (guid != Guid.Empty)
            {
                param.Value = guid;    
            }
            else
            {
                param.Value = DBNull.Value;
            }
        }
    
        public bool IsMutable
        {
            //guid is struct so it's not mutable
            get { return false; }
        }
    
        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 new bool Equals(object x, object y)
        {
            return x != null && x.Equals(y); 
        }
    
        public int GetHashCode(object x)
        {
            return x.GetHashCode();
        }
    }
    

    (Note that I haven't tested it, there are casts here - some tweaking is probably needed)

    In mapping you specify:

    <property name="GuidProperty" column="guidColumn"   type="GuidTypeConverterNamespace.GuidTypeConverter, GuidTypeConverterAssemblyName" />