Search code examples
nhibernatec#-4.0projectionqueryoverselectlist

NHibernate Projections (QueryOver.SelectList) limits


I'm new on stackoverflow and i hope this question will be appreciated.

Simply said: I select everything from table x left outer join table y. Table x has way too many columns so i make new object x. This object is used for the Projection. I can project every single column of table x i want. But when i try to project/select an collection (the collection of table y) then i get the same error: 'Index was outside the bounds of the array'.

My question: Does NHibernate support to select/project a collection at all? Because i've seen this question multiple times by searching on google (and stackoverflow), but none of those questions were answered.

Code example:

public class X
{
    public virtual int ID { get; set; }
    public virtual int IDontNeedMoreInfoAboutClassXItTakesToMuchTimeToRetrieve { get; set; }
    public virtual IList<Y> YCollection { get; set; }
}

public class Y
{
    public virtual int YID { get; set; }
}

public class XRepository{
    public ISession Session {get; set;}
    public IList<X> Get()
    {

        X xAlias = null;
        Y yAlias = null;
        X resultAlias = null;
        return Session.QueryOver<X>()
            .JoinAlias(() => xAlias.YCollection, () => yAlias, JoinType.LeftOuterJoin)
            .SelectList(list => list
                .SelectGroup(() => xAlias.ID).WithAlias(() => resultAlias.ID)
                .SelectGroup(() => xAlias.YCollection).WithAlias(() => resultAlias.YCollection)) // Index was outside the bounds of the array
                .TransformUsing(Transformers.AliasToBean<X>()).List<X>();
    }
}

Solution

  • the results returned have to be grouped with the contents intact (so sql is out since it can only aggregate over the grouped items) but NHibernate can only do this for m,apped entities because there it knows the identity to group by. It is simple to do it manually

    Y yAlias = null;
    var tempResults = Session.QueryOver<X>()
         .JoinAlias(x => x.YCollection, () => yAlias, JoinType.LeftOuterJoin)
         .SelectList(list => list
             .Select(() => xAlias.Id)
             .Select(() => xAlias.Name)
             ...
             .Select(() => yAlias.Id)
             ...
         .ToList<object[]>()
    
    List<X> results = new List<X>(100); // switch to Hashset if too slow and order does not matter
    foreach(var row in tempResults)
    {
        Y y = new Y { Id = (long)row[4], ... };
        X x = results.FirstOrDefault(x => x.Id == (long)row[0]));
        if (x != null)
            x.YCollection.Add(y);
        else
            results.Add(new X { Id = (long)row[0], ..., YCollection = { y } };
    }
    
    return results;