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