Marcin Wolański
"It is not the critic who counts: not the man who points out how the strong man stumbles or where the doer of deeds could have done better. The credit belongs to the man who is actually in the arena [...]"

Mapowanie bool na...

czwartek, 10 grudnia 2009 roku

W Oracle nie ma możliwości przechowywania wartości typu bool. Najbardziej powszechnym rozwiązaniem jest użycie kolumny typu CHAR(1) z wartościami 'Y' i 'N'. Autorzy NHibernate się na to przygotowali udostępniając typ YesNo.

Mój przypadek był inny. Z bazy danych korzystała już aplikacja, która wartości typu bool zapisywała w kolumnach typu INT. Wartość true zdefiniowana była jako -1, wartość false - 0. Moja miała robić tak samo. Da radę. Tylko niestety trzeba się trochę napisać.

Tworzymy klasę MyTrueFalseType implementującą interfejs NHibernate.Types.IUserType:

using System;
using System.Data;
using NHibernate;
using NHibernate.SqlTypes;
using NHibernate.UserTypes;

namespace MyNamespace
{
    public class MyTrueFalseType : IUserType
    {
        private const int TRUE_VALUE = -1;
        private const int FALSE_VALUE = 0;

        public object Assemble(object cached, object owner)
        {
            return cached;
        }

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

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

        public new bool Equals(object x, object y)
        {
            return object.Equals(x, y);
        }

        public int GetHashCode(object x)
        {
            if (x == null)
                return 0;
            return x.GetHashCode();
        }

        public bool IsMutable
        {
            get { return false; }
        }

        public object NullSafeGet(IDataReader rs, string[] names, object owner)
        {
            var obj = NHibernateUtil.String.NullSafeGet(rs, names[0]);

            if (obj == null) return null;

            var boolValue = Int32.Parse(obj.ToString());

            if (boolValue != TRUE_VALUE && boolValue != FALSE_VALUE)
                throw new ADOException(
                    String.Format("Expected data to be {0} or {1} but was {2}.",
                        TRUE_VALUE, FALSE_VALUE, boolValue), null);

            return boolValue == TRUE_VALUE;
        }

        public void NullSafeSet(IDbCommand cmd, object value, int index)
        {
            if (value == null)
            {
                ((IDataParameter)cmd.Parameters[index]).Value = DBNull.Value;
            }
            else
            {
                var boolValue = (bool)value;
                ((IDataParameter)cmd.Parameters[index]).Value =
                    boolValue ? TRUE_VALUE : FALSE_VALUE;
            }
        }

        public object Replace(object original, object target, object owner)
        {
            return original;
        }

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

        public SqlType[] SqlTypes
        {
            get { return new[] { SqlTypeFactory.Int32 }; }
        }
    }
}

Dla przykładowej klasy:

public class MyClass: BaseEntity
{
    public virtual bool Editable { get; set; }
}

definiujemy mapowanie:

public class MyClassMap: ClassMap<MyClass>
{
    public MyClassMap()
    {
        Id(x => x.Id).GeneratedBy.HiLo("1").UnsavedValue("0");
        Map(x => x.Editable).CustomType(typeof(MyTrueFalseType));
        Table("my_class");
    }
}

Od teraz w tabeli my_class w kolumnie editable wartość true będzie zapisywana jako -1, wartość false jako 0.

z tagami .NET and NHibernate

blog comments powered by Disqus