2

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?

crazyTech
  • 1,379
  • 3
  • 32
  • 67
  • There is a solution by developing a customer IUserType class to make use of in your mapping. However, is there a reason you specifically need to use Guid.Empty to indicate nothing instead of using a nullable Guid? – Rich Feb 01 '13 at 21:26

4 Answers4

3

Guid's cannot be null. You can change your type to Nullable<Guid>, or Guid? for short, to allow nulls.

Forty-Two
  • 7,535
  • 2
  • 37
  • 54
  • 1
    Specifically, because `System.Guid` is a struct - or value type - just like an integer or a double. – tomfanning Feb 01 '13 at 20:41
  • @tomfanning Sorry, I worded my question in the wrong way. 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? – crazyTech Feb 01 '13 at 20:47
  • @user1338998 Got you. Typed out an answer for you. – tomfanning Feb 01 '13 at 21:08
  • He's not asking to change his POCO, he's asking for NULLs in the database to be translated to Guid.Empty and vice versa. – Rich Feb 01 '13 at 21:21
3

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" />
pkmiec
  • 2,594
  • 18
  • 16
  • 2
    This seems like a lot of code to do something so simple. I think I'm going off NHibernate. – tomfanning Feb 02 '13 at 21:26
  • Well, maybe it is, but I just wrap the boilerplate code into base class in my projects (ImmutableTypeConverter). I don't find it a reason to go off NHibernate just because of this -- "featurerichness" has some costs. – pkmiec Feb 03 '13 at 10:38
  • @pkmiec Thanks for the code. I took your advice, but tomfanning seems to bring up a good point. It is quite a lot of work for something simple. – crazyTech Feb 06 '13 at 03:40
  • Saved the day for me! Thanks!! – Paul Aldred-Bann Jul 05 '16 at 12:30
2

What you want to do is create a mapping between Guid (not nullable Guids) and GUIDs in the database. This is the purpose of the IUserType interface in NHibernate. You would need a custom GuidUserType class that does the translation and then refer to it in the mapping definition (hbxml, fluent, whatever). See http://blog.miraclespain.com/archive/2008/Mar-18.html for a full explanation.

Rich
  • 2,076
  • 1
  • 15
  • 16
0

In the mapping for the primary key of your Department object, there should be a property called unsaved-value. Set that to the value of Guid.Empty and all will be fine.

Below is an example of how you'd do it using raw hbm mapping:

<id name="DepartmentId" type="Guid" unsaved-value="00000000-0000-0000-0000-000000000000">
    <generator class="guid" />
</id>
Thilak Nathen
  • 1,333
  • 1
  • 8
  • 13