c# - NHibernate Group By 父实体没有 N+1 查询?

标签 c# nhibernate

我有两个具有父子关系的表。我想计算子表的记录,按父实体对它们进行分组并收集结果。所以我想看看每个父实体在子表中被引用了多少次。

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

关于c# - NHibernate Group By 父实体没有 N+1 查询?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21440711/

相关文章:

c# - Get<T> 方法如何与 DynamicParameters 一起使用?

c# - 如何使用用户生成的整数数组填充 dataGridView

database - 遗留数据库、Fluent NHibernate 和测试我的映射

c# - 在 HQL 查询中使用 nhibernate <loader> 元素

c# - 手动实现 string.IndexOf

c# - .NET 4.0、MVC 2、 Entity Framework 4 和存储库模式

winforms - NHibernate、WinForms 和 DataBinding - 它们可以很好地协同工作吗?

linq - F# linq 查询和可为空的强制转换

nhibernate - AppFabric:无法联系缓存服务

c# - Windows Phone 7 和套接字 : What am I doing wrong?