简体   繁体   English

NHibernate Group没有N + 1查询的父实体?

[英]NHibernate Group By parent entity without N+1 query?

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: 所以如果我的父表是Cats:

| Id | Name     |
|  1 | Bob      |
|  2 | Garfield |

and the child table is CatSkills: 子表是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. 我试过NHibernate LINQ,查询似乎是正确的,但我得到一个“功能不受支持”的例外。

I tried with NHibernate QueryOver, there I get a N+1 problem: 我尝试使用NHibernate QueryOver,在那里我遇到了N + 1问题:

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. 在实验的其他部分,我最终得到一个SQL异常,关于SELECT语句中引用的列如何不是GROUP BY子句的一部分。

Does anyone have an idea on how to implement this query? 有没有人知道如何实现这个查询? Thanks! 谢谢!

Update 更新

The updated code, thanks to Radim, looks like this: 感谢Radim,更新后的代码如下所示:

// 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. 所以这摆脱了N + 1问题,但我必须手动将父类(示例中的Cat)的每个属性映射到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. 如果我可以将它映射为.Select(s => s)但是会抛出一个异常,说它无法映射“”属性。

An elegant way could be to directly query the parent Cat , and extend it with the required count - as a subselect. 一种优雅的方式可以是直接查询父Cat ,并使用所需的计数扩展它 - 作为子选择。

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 所以在这种情况下,我们确实希望,Parent确实拥有一个属性

public virtual int Count { get; set ;}

which is not mapped by NHiberante. 这不是由NHiberante映射的。 If we cannot extend the C# object, we can create some CatDTO (having same properties as Cat entity - plus the Count ) 如果我们不能扩展C#对象,我们可以创建一些CatDTO (具有与Cat实体相同的属性 - 加上Count

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM