NHibernate Group By parent entity without N+1 query?

c#, nhibernate

Solution

An elegant way could be to directly query the parent `Cat`, and extend it with the required count - as a subselect.

Cat parent = null;
CatSkills child = null;

// this is in fact a subselect, which will be injected into parent's SELECT
var subQuery = QueryOver.Of<CatSkills>(() => child)
    .Where(() => child.Cat.ID == parent.ID)
    .Select(Projections.RowCount());

// the alias here is essential, because it is used in the subselect
var query = session.QueryOver<Cat>(() => parent);

query.SelectList(l => l
    .Select(p => p.ID).WithAlias(() => parent.ID)
    .Select(p => p.Name).WithAlias(() => parent.Name)
    // see the parent.Count property
    .Select(Projections.SubQuery(subQuery)).WithAlias(() => parent.Count)
    );
query.TransformUsing(Transformers.AliasToBean<Cat>());

So in this case, we do expect, that Parent does have a property

public virtual int Count { get; set ;}

which is not mapped by NHiberante. If we cannot extend the C# object, we can create some `CatDTO` (having same properties as `Cat` entity - plus the `Count`)

Problem

I have two tables that have a parent-child relationship. I want to count the records of the child table, grouping them by the parent entity and gather the results. So I want to see how many times each parent entity is referenced in the child table. So if my parent table is Cats: ``` | Id | Name | | 1 | Bob | | 2 | Garfield | ``` and the child table is CatSkills: ``` | Id | Cat_Id | Skill | | 1 | 1 | Land on feet | | 2 | 2 | Eat lasagne | | 3 | 2 | Escape diets | ``` I want to receive this: ``` | Id | Name | count of skills | | 1 | Bob | 1 | | 2 | Garfield | 2 | ``` I've tried with NHibernate LINQ, the query seems to be correct, but I get a "feature not supported" exception. I tried with NHibernate QueryOver, there I get a N+1 problem: ``` var q = Session.QueryOver<CatSkill>() .Fetch(s => s.Cat).Eager .Select(Projections.ProjectionList() .Add(Projections.Group<CatSkill>(s => s.Cat)) .Add(Projections.RowCount())) .List<object[]>(); ``` The above query works but will fetch all parent records in separate queries. In other parts of experimenting I ended up with a SQL exception about how the referenced columns in the SELECT statement are not part of the GROUP BY clause. Does anyone have an idea on how to implement this query? Thanks! Update The updated code, thanks to Radim, looks like this: ``` // a private class, just to make the query work class CatDto : Cat { public int Count { get; set; } } // the actual query code Cat parent = null; CatSkill child = null; CatDto dto = null; // this is in fact a subselect, which will be injected into parent's SELECT var subQuery = QueryOver.Of<CatSkill>(() => child) .Where(() => child.Cat.ID == parent.ID) .Select(Projections.RowCount()); // this is another subquery to filter out cats without skills var skillFilterSubQuery = QueryOver.Of<CatSkill>(() => child) .Where(() => child.Cat.ID == parent.ID /* && more criteria on child table here... */) .Select(p => p.Cat); // the alias here is essential, because it is used in the subselect var query = session.QueryOver<Cat>(() => parent); // I only want cats with skills query = query.WithSubquery.WhereExists(skillFilterSubQuery); query.SelectList(l => l .Select(p => p.ID).WithAlias(() => dto.ID) .Select(p => p.Name).WithAlias(() => dto.Name) // annoying part: I have to repeat the property mapping for all needed properties of parent... // see the parent.Count property .Select(Projections.SubQuery(subQuery)).WithAlias(() => dto.Count)); query.TransformUsing(Transformers.AliasToBean<CatDto>()); return query.List<CatDto>(); ``` So this gets rid of the N+1 problem but I have to map every property of the parent class (Cat in the example) manually to the DTO. It would be nice if I could map it like `.Select(s => s)` but that throws an Exception saying it can't map the "" property.

Original source