[英]Nhibernate n+1 with ternary relationship. Want the middle entity in the ternary
[英]NHibernate Group By parent entity without N+1 query?
我有两个有父子关系的表。 我想计算子表的记录,由父实体对它们进行分组并收集结果。 所以我想看看子表中引用每个父实体的次数。
所以如果我的父表是Cats:
| Id | Name |
| 1 | Bob |
| 2 | Garfield |
子表是CatSkills:
| Id | Cat_Id | Skill |
| 1 | 1 | Land on feet |
| 2 | 2 | Eat lasagne |
| 3 | 2 | Escape diets |
我想收到这个:
| Id | Name | count of skills |
| 1 | Bob | 1 |
| 2 | Garfield | 2 |
我试过NHibernate LINQ,查询似乎是正确的,但我得到一个“功能不受支持”的例外。
我尝试使用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[]>();
上述查询有效但会在单独的查询中获取所有父记录。
在实验的其他部分,我最终得到一个SQL异常,关于SELECT语句中引用的列如何不是GROUP BY子句的一部分。
有没有人知道如何实现这个查询? 谢谢!
更新
感谢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>();
所以这摆脱了N + 1问题,但我必须手动将父类(示例中的Cat)的每个属性映射到DTO。
如果我可以将它映射为.Select(s => s)
但是会抛出一个异常,说它无法映射“”属性。
一种优雅的方式可以是直接查询父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>());
所以在这种情况下,我们确实希望,Parent确实拥有一个属性
public virtual int Count { get; set ;}
这不是由NHiberante映射的。 如果我们不能扩展C#对象,我们可以创建一些CatDTO
(具有与Cat
实体相同的属性 - 加上Count
)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.