Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using NHibernate ICompositeUserType with a value type

I have a domain model object which has properties of type System.DateTimeOffset. I'm using a database which doesn't support this type natively, so I'm planning to store it using a column of type 'datetime' and one of type 'smallint'.

I've dug around on how to map this using NHibernate components, and found that it could work using an ICompositeUserType instance. However, upon implementing the interface, I've come across the method called "SetPropertyValue" which ostensibly sets a property within the type. Since DateTimeOffset is a System.ValueType, just setting a property like this won't work since it is immutable (at least, without using some reflection or unsafe code, which I'd like to avoid). Since the instance parameter on SetPropertyValue isn't 'ref' how does one use ValueType instances as components in NHibernate?

like image 850
codekaizen Avatar asked Jul 27 '09 17:07

codekaizen


2 Answers

Make the user type immutable by returning false in IsMutable and just throw an exception in SetPropertyValue.

I have something similar, but with an own datatype instead of DateTimeOffset. I just adapted the code for you. It stores the date as UTC time and the offset as TimeSpan (stores Ticks. Of course you don't need this resolution. But, you should not store whole hours for time zones, there are time zones offsets with fractions of hours!! And TimeSpan is build in an works out of the box.)

public class DateTimeOffsetUserType : ICompositeUserType
{
    public bool IsMutable
    {
        get { return false; }
    }

    public void SetPropertyValue(object component, int property, object value)
    {
        throw new InvalidOperationException("Immutable, SetPropertyValue is not allowed");
    }

    public object NullSafeGet(System.Data.IDataReader dr, string[] names, NHibernate.Engine.ISessionImplementor session, object owner)
    {
        if (dr == null)
        {
            return null;
        }

        DateTime? utcTime;
        TimeSpan? offset;

        utcTime = (DateTime?)PropertyTypes[0].NullSafeGet(dr, names[0], session, owner);
        offset = (TimeSpan?)PropertyTypes[1].NullSafeGet(dr, names[1], session, owner);

        if (utcTime == null || offset == null) return null;
        return new DateTimeOffset(utcTime.Value, offset.Value);
    }

    public void NullSafeSet(System.Data.IDbCommand cmd, object value, int index, NHibernate.Engine.ISessionImplementor session)
    {
        if (value == null)
        {
            NHibernateUtil.Timestamp.NullSafeSet(cmd, null, index);
            NHibernateUtil.TimeSpan.NullSafeSet(cmd, null, index + 1);
        }
        else
        {
            DateTimeOffset dateTimeOffset = (DateTimeOffset)value;

            PropertyTypes[0].NullSafeSet(cmd, dateTimeOffset.UtcDateTime, index, session);
            PropertyTypes[1].NullSafeSet(cmd, dateTimeOffset.Offset, index + 1, session);
        }

    }

    // other methods
like image 188
Stefan Steinegger Avatar answered Sep 28 '22 11:09

Stefan Steinegger


It's my understanding that if the type you're mapping with and ICompositeUserType is immutable, SetPropertyValue() should do nothing at all, or even throw an exception, since nhibernate shouldn't be calling it.

like image 21
Keith Klein Avatar answered Sep 28 '22 12:09

Keith Klein