3
votes

I've got an object that uses System.Version as a property. I want this object to be mapped into my table, storing the version as a string. what's the best way to go about doing this with NHibernate v1.2?

public class MyClass
{
  public Version MyVersion {get; set;}
}

not sure what to do with the propery mapping

<property name="MyVersion" column="MyVersion" not-null="true" />

this gives me errors saying "Invalid attempt to GetBytes on column 'MyVersion0_0_'. The GetBytes function can only be used on columns of type Text, NText, or Image." If I use type="string" in the map, i get casting errors.

suggestions?

4

4 Answers

4
votes

You would need to write an NHibernate user type, that returns a property value of type Version, but persists as a string. There is a skeleton user type implementation here that takes care of some of the work for you.

2
votes

David M's suggestion and article link were great. Just in case anyone else has problems getting to that site, though ( i had to load it up from google cache), here is my solution:


    public abstract class BaseImmutableUserType : IUserType
    {
        public abstract object NullSafeGet(IDataReader rs, string[] names, object owner);
        public abstract void NullSafeSet(IDbCommand cmd, object value, int index);
        public abstract SqlType[] SqlTypes { get; }

        public new bool Equals(object x, object y)
        {
            if (ReferenceEquals(x, y))
            {
                return true;
            }

            if (x == null || y == null)
            {
                return false;
            }

            return x.Equals(y);
        }

        public int GetHashCode(object x)
        {
            return x.GetHashCode();
        }

        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 DeepCopy(cached);
        }

        public object Disassemble(object value)
        {
            return DeepCopy(value);
        }

        public Type ReturnedType
        {
            get { return typeof(T); }
        }

        public bool IsMutable
        {
            get { return false; }
        }
    }

    public class VersionUserType: BaseImmutableUserType
    {

        public override SqlType[] SqlTypes 
        { 
            get
            {
                return new SqlType[]{new StringSqlType()};
            } 
        }

        public override object NullSafeGet(IDataReader rs, string[] names, object owner)
        {
            Version version = null;
            var value = NHibernateUtil.String.NullSafeGet(rs, names) as string;
            if (value != null)
                version = new Version(value);
            return version;
        }

        public override void NullSafeSet(IDbCommand cmd, object value, int index)
        {
            object valueToSet;
            Version version = value as Version;
            if (version != null)
            {
                valueToSet = version.ToString();
            }
            else
            {
                valueToSet = DBNull.Value;
            }

            NHibernateUtil.String.NullSafeSet(cmd, valueToSet, index);
        }
    }

and the mapping file now has this property:

<property name="MyVersion" column="MyVersion" not-null="true" type="My.Namespace.VersionUserType, My.Assembly" />

2
votes

Another thumbs-up for David M's answer. However, you'll note that System.Version has a constructor that takes a string representation, as well as a ToString() that produces the same format, so you could map the property as a simple string in NHibernate and map it to a private string field directly, then have a facade property typed to System.Version something like this:

public System.Version Version
{
    get { return new System.Version(_versionAsString); }
    set { _versionAsString = value.ToString(); }
}

I don't see it as bad practice particularly, and it's easy enough to do. You might need to use an internal property rather than a field if you're going to proxy the type for lazy-loading, but otherwise it should work.

0
votes

David M's answer is probably the most robust way to go about it.

As an alternative, you could convert the property to a string and just use that. I'm not sure how big a change that is in your model though.