NHibernate QueryOver组没有选择按列分组
有如下查询:
var subquery = SessionFactory.GetCurrentSession() .QueryOver() .Where(_ => _.SomeOtherEntity.Id == someId) .SelectList(list => list .SelectGroup(x => x.SomeGroupByProperty) .SelectMax(x => x.MaxPerGroupProperty)) .List();
生成的sql正在选择SomeGroupByProperty
和SomeGroupByProperty
最大值。 是否可以让它在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 ProjectionList
与ToSqlString
, GetTypedValues
和GetTypedValues
方法的实现略有不同。
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); } }