NHibernate QueryOver组没有选择按列分组

有如下查询:

var subquery = SessionFactory.GetCurrentSession() .QueryOver() .Where(_ => _.SomeOtherEntity.Id == someId) .SelectList(list => list .SelectGroup(x => x.SomeGroupByProperty) .SelectMax(x => x.MaxPerGroupProperty)) .List(); 

生成的sql正在选择SomeGroupByPropertySomeGroupByProperty最大值。 是否可以让它在SomeGroupByProperty上进行分组,但只选择MaxPerGroupProperty最大值? 这是为了将子查询结果与父查询中的包含一起使用。

这是NHibernate jira(标准查询)中的一个未解决的问题: https : //nhibernate.jira.com/browse/NH-1426

你可以这样做

 var subquery = QueryOver.Of() .Where(_ => _.SomeOtherEntity.Id == someId) .Select( Projections.ProjectionList() .Add(Projections.SqlGroupProjection("max(MaxPerGroupProperty) as maxAlias", "SomeGroupByProperty", new string[] { "maxAlias" }, new IType[] { NHibernate.NHibernateUtil.Int32 }))); var parentQuery = session.QueryOver() .WithSubquery.WhereProperty(x => x.MaxPerGroupPropertyReference).In(subquery).List(); 

不像使用实体属性那么漂亮,但确实有效。

我知道,我正在展示替代方案,而不是给出答案。 但是如果我们需要在另一个查询中使用这样的子查询,我们可以:

  • 使用外部查询中的某些设置过滤内部查询
  • 使用Exists (可以返回任意数量的列)

因此,我们需要首先定义Alias (外部查询)并进行一些过滤:具有如下查询:

 SomeOtherEntity otherEntity = null; // alias of the outer query var subquery = QueryOver .QueryOver.Of()() .Where(_ => _.SomeOtherEntity.Id == someId) .Where(_ => _.SomeOtherEntity.Id == otherEntity.Id) // here we consume outer alias .SelectList(list => list .SelectGroup(x => x.SomeGroupByProperty) .SelectMax(x => x.MaxPerGroupProperty) ); 

后来,在外(根)查询中,我们可以像这样使用它:

 var query = session .QueryOver(() => otherEntity) .WithSubquery .WhereExists(subquery); 

这是使用WHERE子句的超简化解决方案。 我们很可能需要在HAVING子句中应用该filter。 这是一个详细的例子如何:

查询HasMany引用

所以,子查询看起来像这样:

 // this WHERE // .Where(_ => _.SomeOtherEntity.Id == otherEntity.Id) // here we consume // into this HAVING .Where(Restrictions.EqProperty( Projections.Max(x => x.MaxPerGroupProperty), Projections.Property(() => otherEntity.GroupProperty) )) 

这里有完整的例子

您可以使用自己的投影来完成它,这类似于NHibernate ProjectionListToSqlStringGetTypedValuesGetTypedValues方法的实现略有不同。

 public static class CustomProjections { public static ProjectionWithGroupByWithoutSelectProjection GroupByWithoutSelect(IProjection projection, params Expression>[] groupByExpressions) { var projectionRet = new ProjectionWithGroupByWithoutSelectProjection(); projectionRet.Add(projection); foreach (var groupByExpression in groupByExpressions) { projectionRet.Add(Projections.Group(groupByExpression)); } return projectionRet; } } public class ProjectionWithGroupByWithoutSelectProjection : IProjection { // because ProjectionList constructor is protected internal private class ProjectionListCustom : ProjectionList { } private readonly ProjectionList _projectionList = new ProjectionListCustom(); protected internal ProjectionWithGroupByWithoutSelectProjection() { } public ProjectionWithGroupByWithoutSelectProjection Add(IProjection proj) { _projectionList.Add(proj); return this; } public ProjectionWithGroupByWithoutSelectProjection Add(IProjection projection, string alias) { _projectionList.Add(projection, alias); return this; } public ProjectionWithGroupByWithoutSelectProjection Add(IProjection projection, Expression> alias) { _projectionList.Add(projection, alias); return this; } public IType[] GetTypes(ICriteria criteria, ICriteriaQuery criteriaQuery) { return _projectionList[0].GetTypes(criteria, criteriaQuery); } public SqlString ToSqlString(ICriteria criteria, int loc, ICriteriaQuery criteriaQuery, IDictionary enabledFilters) { return _projectionList[0].ToSqlString(criteria, loc, criteriaQuery, enabledFilters); } public SqlString ToGroupSqlString(ICriteria criteria, ICriteriaQuery criteriaQuery, IDictionary enabledFilters) { return _projectionList.ToGroupSqlString(criteria, criteriaQuery, enabledFilters); } public string[] GetColumnAliases(int position, ICriteria criteria, ICriteriaQuery criteriaQuery) { return _projectionList.GetColumnAliases(position, criteria, criteriaQuery); } public string[] GetColumnAliases(string alias, int position, ICriteria criteria, ICriteriaQuery criteriaQuery) { return _projectionList.GetColumnAliases(alias, position, criteria, criteriaQuery); } public IType[] GetTypes(string alias, ICriteria criteria, ICriteriaQuery criteriaQuery) { return _projectionList[0].GetTypes(alias, criteria, criteriaQuery); } public string[] Aliases => _projectionList.Aliases; public override string ToString() { return _projectionList.ToString(); } public bool IsGrouped => _projectionList.IsGrouped; public bool IsAggregate => _projectionList.IsAggregate; public TypedValue[] GetTypedValues(ICriteria criteria, ICriteriaQuery criteriaQuery) { return _projectionList[0].GetTypedValues(criteria, criteriaQuery); } }