簡體   English   中英

NHibernate Group沒有N + 1查詢的父實體?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM