NZJames
NZJames

Reputation: 5055

Nhibernate - mapping generic IList objects to comma separated lists of Keys in single DB column

I may be trying to be too ambitious here but I'm hoping someone might be able to suggest a solution.

I have a number of UserChoice objects, such as TravellingChoices, SocialisingChoices, SportChoices etc and in a users profile, each user specifies exactly which one they fall under, so in the DB they have a single profile row mapped with INT ids corresponding to the keys of the choice options they have chosen.

When you load the profile up with NHibernate, I use many-to-one mappings to build up a profile object which actually contains the TravellingChoice, SocialisingChoice, SportChoice etc objects themselves, not the id's, which is what I want.

The problem is I then want people to be able to search for other users who fit certain criteria, but you are allowed to select multiple ones. Eg users with either one of two different travelling choices, or you can choose 3 sport choices to search on.

So the search criteria object I wanted to have in memory as a number of strongly typed lists eg

IList<TravellingChoice>
IList<SocialisingChoice>

and so forth.

The problem is I am storing the search criteria object in db as a number of columns with id separated lists eg

SocialisingChoicesColumn : 1,2,6
TravellingChoicesColumn : 5,8

etc..

What I want to be able to do is have an nhibernate mapping take my table with columns full if id/comma lists, and translate each one into the relevant Typed Ilist with fully populated objects.

So how do I go from the above table with id/comma lists, to an object with

IList<TravellingChoice>
IList<SocialisingChoice>

etc.. properties?

Is this possible or too complex for Nhibernate?

Upvotes: 3

Views: 1085

Answers (1)

Firo
Firo

Reputation: 30813

yes with ICompositeUserType. you can build on the this

class MyUserType<TChoice> : ICompositeUserType
{
    public object Assemble(object cached, ISessionImplementor session, object owner)
    {
        return DeepCopy(cached);
    }

    public object DeepCopy(object value)
    {
        return ((IList<TChoice>)value).ToList();
    }

    public object Disassemble(object value, ISessionImplementor session)
    {
        return DeepCopy(value);
    }

    bool ICompositeUserType.Equals(object x, object y)
    {
        var list1 = x as IList<TChoice>;
        var list2 = y as IList<TChoice>;

        return (x == null) ? y == null : list1.SequenceEqual(list2);
    }

    public int GetHashCode(object x)
    {
        // example implementation
        var list = x as IList<TChoice>;
        unchecked
        {
            return list == null ? 0 : list.Sum(choice => choice.GetHashCode());
        }
    }

    public object GetPropertyValue(object component, int property)
    {
        // the list has no properties to get
        throw new NotSupportedException();
    }

    public bool IsMutable
    {
        get { return true; }
    }

    public object NullSafeGet(IDataReader dr, string[] names, ISessionImplementor session, object owner)
    {
        var str = (string)NHibernateUtil.String.Get(dr, names[0]);
        IList<int> ids = str.Split(',').Select(id => int.Parse(id.Trim())).ToList();
        // HACK: assuming session also implements ISession
        return ((ISession)session).QueryOver<TChoice>()
            .WhereRestrictionOn(choice => choice.Id).IsInG(ids)
            .List();
    }

    public void NullSafeSet(IDbCommand cmd, object value, int index, bool[] settable, NHibernate.Engine.ISessionImplementor session)
    {
        var list = value as IList<TChoice>;
        NHibernateUtil.String.Set(cmd, string.Join(", ", list.Select(choice => choice.Id.ToString()).ToArray()), index);
    }

    public string[] PropertyNames
    {
        get { return new string[0]; }
    }

    public IType[] PropertyTypes
    {
        get { return new IType[0]; }
    }

    public object Replace(object original, object target, NHibernate.Engine.ISessionImplementor session, object owner)
    {
        return original;
    }

    public Type ReturnedClass
    {
        get { return typeof(IList<TChoice>); }
    }

    public void SetPropertyValue(object component, int property, object value)
    {
        // the list has no properties to set
        throw new NotSupportedException();
    }
}

Upvotes: 4

Related Questions