Geeks With Blogs

News


Mike Nichols - SonOfNun Technology If I were the captain on a big steamboat...

In a previous post I proposed a Generic class for building custom types using NHibernate. This lets us keep our Domain entities free from requiring hidden fields or other methods to persist Value Objects. While a Component may be used, this little Generic class lets us keep the power of HQL for our complex types.

One situation that is not addressed in the Hibernate in Action book, though, is what to do when you want to combine these Value Objects into one Value Object. For example, if we have a Money Value Object as in this post and then want to combine that with an RateFactor object:

    public class RateFactor : IFactor

    {

        public string Name

        {

            get { return _name; }

        }

 

        private string _name;

 

        private RateFactor(string name)

        {

            _name = name;

        }

        public static readonly RateFactor HOUR = new RateFactor("Hourly");

        public static readonly RateFactor YEAR = new RateFactor("Annually");

        private static readonly ValueObjectFieldBrowser<RateFactor> list =

           new ValueObjectFieldBrowser<RateFactor>();

        public static IList<RateFactor> LIST_ALL

        {

            get

            {

                return list.All();

            }

        }

        public override string ToString()

        {

            return _name;

        }

 

        public override bool Equals(object obj)

        {

            if (obj == null) return false;

            if (ReferenceEquals(this, obj)) return true;

            if (GetType() != obj.GetType()) return false;

 

            RateFactor e = (RateFactor) obj;

            return e._name.Equals(_name);

        }

 

        public override int GetHashCode()

        {

            return 29*_name.GetHashCode();

        }

    }

Then we want to combine these together to form a MoneyRate Value Object:

    public class MoneyRate : IRate<Money>

    {

        public static MoneyRate HOURLY(double amount)

        {

            return new MoneyRate(amount,RateFactor.HOUR);

        }

        public static MoneyRate ANNUALLY(double amount)

        {

            return new MoneyRate(amount,RateFactor.YEAR);

        }

        private MoneyRate(double amount,RateFactor factor)

        {

            _money = new Money(amount);

            _factor = factor;

        }

        private readonly Money _money;

        private readonly RateFactor _factor;

 

        public Money Amount

        {

            get

            {

                return _money;

            }

        }

 

        public IFactor Factor

        {

            get { return _factor; }

        }

 

        public override bool Equals(object obj)

        {

            if (obj == null) return false;

            if (ReferenceEquals(this, obj)) return true;

            if (GetType() != obj.GetType()) return false;

 

            MoneyRate e = (MoneyRate) obj;

            return e._money.Equals(_money) &&

                   e._factor.Equals(_factor);

        }

 

        public override int GetHashCode()

        {

            return 29*29*_money.GetHashCode() +

                   29*_factor.GetHashCode();

        }

 

        public override string ToString()

        {

            return _money.ToString() + " " + _factor.ToString();

        }

 

    }

So now I have TWO ValueObjects, each with their own ICompositeUserType implementations within the same object that (what else) wants to have ITS own ICompositeUserType. How to do it? Using my GenericCompositeUserType<T>, I simply need to define the nested composites using the NHibernateUtil.CustomType(Type type) IType.

    public class MoneyRateUserType : GenericCompositeUserType<MoneyRate>

    {

        public MoneyRateUserType()

            : this(false, new IType[2] { NHibernateUtil.Custom(typeof(MoneyUserType)), NHibernateUtil.Custom(typeof(RateFactorUserType)) },

            new string[2] { "Amount", "Factor" }, null,null)

        {

        }

        public MoneyRateUserType(bool isMutable, IType[] propertyTypes,

                                   string[] propertyNames, GetMap getMap, DeepCopyMap deepCopyMethod)

            : base(isMutable, propertyTypes, propertyNames, getMap,deepCopyMethod)

        {

        }

 

    }

If you look in the first constructor, you the two types' ICompositeUserType implementations being referenced (not the types themselves).

Finally, in the mapping we can just use this to have an HourlyRate property mapped to my DB.

    <property name="MoneyRate" type="Cei.eMerge.Data.NHibernateImpl.Types.MoneyRateUserType,Cei.eMerge.Data">

      <column name="MoneyRateAmount"/>

      <column name="MoneyRateFactor"/>

    </property>

Now I can try it out with

        [Test]

        public void CanSaveMoneyRate()

        {

            Domain.ValueObject val = new ValueObject(MoneyRate.HOURLY(30));

            NHibernateSessionManager.Instance.GetSession().Save(val);

 

        }

And WALAAH! There my values are in the database. NHibernate rocks.

Posted on Wednesday, August 30, 2006 12:25 PM O/R Mappers , C# | Back to top


Comments on this post: NHibernate Composite User Type (ICompositeUserType) gets even better

No comments posted yet.
Your comment:
 (will show your gravatar)


Copyright © Mike Nichols | Powered by: GeeksWithBlogs.net