views:

635

answers:

1

Table:

CREATE TABLE Instrument
(   Id    INT IDENTITY
,   Name   VARCHAR(50)
,   Tenor   VARCHAR(10)
//...

Model:

    interface ITenor
    {
        int Length { get; }
        string ToString();
    }

    class DayTenor : ITenor
    {
        public int Length
        {
            get { return 1; }
        }

        public override string ToString()
        {
            return "DAY";
        }
    }

    class MonthTenor : ITenor
    {
        public int Length
        {
            get { return 30; }
        }

        public override string ToString()
        {
            return "MONTH";
        }
    }

    class TenorFactory
    {
        ITenor GetTenor(string tenorString)
        {
            switch (tenorString)
            {
                case "DAY":
                    return new DayTenor();
                    break;
                case "MONTH":
                    return new MonthTenor();
                    break;
                default:
                    throw new NotImplementedException(string.Format( "Tenor {0} is not implemented", tenorString ));
            }
        }
    }

    class Instrument
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public ITenor Tenor { get; set; }
    }

    class InstrumentMap : ClassMap<Instrument>
    {
        public InstrumentMap()
        {
            WithTable( "Instrument" );
            Id( x => x.Id );
            Map( x => x.Name );
            Map( x => x.Tenor );
        }
    }

This is a great simplification of the problem domain.

The line Map( x => x.Tenor ); will clearly not work, as you cannot implicitly convert the VARCHAR column to an ITenor type. Is there a way I can map to automatically use the Factory to get the ITenor it requires for the specified string in the DB, and use the ToString() from the ITenor class to revert back to the DB?

If not, what refactoring would you recommend to make this feasable? Many thanks

+2  A: 

A possible solution is to keep the string as it is in the database and map it to a TenorString property. Then add another typed property that simply does the transformation between the string and the typed value:

class Instrument
{
    public string TenorString { get; set; }
    public ITenor Tenor { 
        get { return GetTenor(this.TenorString); } 
        set { TenorString = value.ToString() }; 
    }
}

TenorString is mapped normaly but Tenor property is not mapped at all:

Map( x => x.TenorString );

Off course the Tenor get property can return a cached value that is reset when TenorString changes to avoid the factory call every time.

Aleris
This is probably the easiest method. You could also create a custom type by implementing IUserType: http://www.hibernate.org/hib_docs/nhibernate/1.2/reference/en/html/mapping.html#mapping-types-custom
Stuart Childs
Many thanks Aleris and Stuart. I'll rather implement IUserType as I do not want to hack the Instrument class, but it is a very simple workaround.
Hendrik