我查看了类似的问题,但无法找到简单的解释。我本可以错过它,但我保证我看了。实际上,除了一篇博文之外,我甚至无法找到文档,这些博文会迅速掩盖所有内容并假设您熟悉其他形式的NH。
鉴于Program
和Topic
之间的多对多,后者位于Topics
的层次结构中,我想要检索所有Programs
给定Topic
,可能包括其子主题。由于程序可能列在给定父主题的多个子主题下,因此我需要使用子查询或处理必须使用distinct(并且TransformUsing(Transformers.DistinctRootEntity)
的简单方法不起作用)。< / p>
原始SQL应该类似于
SELECT ProgramId, Title, bar, baz, foo FROM Programs
WHERE ProgramId IN
(SELECT ProgramId from Program_Topics WHERE TopicId IN (1, 2, ...))
将结果转换为模型类型以传输到视图。我最初的尝试是:
ProgramDTO pDTO = null;
/* topicIds is List<int> passed into function */
var query = Session.QueryOver<Program>()
.JoinQueryOver<Topic>(p => p.Topics)
.WhereRestrictionOn(pt => pt.Id).IsInG<int>(topicIds)
.TransformUsing(Transformers.DistinctRootEntity)
.SelectList(list => list
.Select(program => program.Id).WithAlias(() => pDTO.Id)
.Select(program => program.Title).WithAlias(() => pDTO.Title)
.Select(program => program.Location).WithAlias(() => pDTO.Location)
.Select(program => program.Description).WithAlias(() => pDTO.Description)
)
.TransformUsing(Transformers.AliasToBean(typeof(ProgramDTO)));
return query.List<ProgramDTO>();
显然这会运行连接而不是子查询,但我找不到像这样多对多的子查询的示例。
public class Program : Entity {
public virtual ISet<Topic> Topics { get; protected internal set; }
...
}
public class Topic : Entity {
public virtual ISet<Program> Programs { get; protected internal set; }
public virtual Topic ParentTopic { get; protected internal set; }
...
}
答案 0 :(得分:12)
您需要创建一个包含Id的dettached查询,然后将此子查询与主查询一起使用。
我在此处粘贴了一个示例,因此您需要将相关位替换为您的类名等。
首先设置(你可以忽略这一点): -
public class TestDto {
public long Id { get; set; }
public string Name { get; set; }
}
...
TestDto dto = null;
var ids = new List<int> { 1,2,5,7 };
现在发布的查询: -
var idSubQuery = QueryOver.Of<CmsRegionContent>()
.WhereRestrictionOn(w => w.Id).IsIn(ids)
.Select(Projections.Distinct(Projections.Property<CmsPage>(s => s.Id)));
最后一点是把它们放在一起: -
var query = Session.QueryOver<CmsPage>()
.JoinQueryOver<CmsRegionContent>(l => l.CmsRegionContentList)
.WithSubquery
.WhereProperty(m => m.Id)
.In(idSubQuery)
.SelectList(list => list
.Select(p => p.Id).WithAlias(() => dto.Id)
.Select(p => p.PageName).WithAlias(() => dto.Name)
)
.TransformUsing(Transformers.AliasToBean(typeof(TestDto)));
var model = query.List<TestDto>();
这将创建以下SQL: -
SELECT
this_.Id as y0_,
this_.PageName as y1_
FROM cmspage this_ inner join cmsregioncontent cmsregionc1_
on this_.Id=cmsregionc1_.PageId
WHERE cmsregionc1_.Id in (
SELECT
distinct this_0_.Id as y0_
FROM cmsregioncontent this_0_
WHERE this_0_.Id in (
1 /* ?p0 */,
2 /* ?p1 */,
5 /* ?p2 */,
7 /* ?p3 */)
)
希望您能够按照您的班级/属性名称进行此操作。
答案 1 :(得分:4)
好吧,对此更进一步,虽然我不喜欢结果的一部分,但确实有效:
var distinctProgIdsSubQuery = QueryOver.Of<Program>().
JoinQueryOver<Topic>(p => p.Topics).
WhereRestrictionOn(pt => pt.Id).IsIn(topicIds)
.Select(Projections.Distinct(Projections.Property<Program>(p => p.Id)));
ProgramDTO pDTO = null;
var progQuery = Session.QueryOver<Program>()
.WithSubquery.WhereProperty(p => p.Id).In(distinctProgIdsSubQuery)
.SelectList(list => list
.Select(program => program.Id).WithAlias(() => pDTO.Id)
.Select(...)
)
.TransformUsing(Transformers.AliasToBean(typeof(ProgramDTO)));
return progQuery.List<ProgramDTO>();
这会产生
SELECT this_.ProgramId as y0_, ...
FROM Programs this_
WHERE this_.ProgramId in (
SELECT distinct this_0_.ProgramId as y0_
FROM
Programs this_0_
inner join
Programs_Topics topics3_
on this_0_.ProgramId=topics3_.ProgramId
inner join
Topics topic1_
on topics3_.TopicId=topic1_.TopicId
WHERE
topic1_.TopicId in (
@p1, @p2, ...
)
)
这可能是NH的限制,但是没有需要来加入子查询中的 Programs 表。我尝试从另一个方向写这个 - 也就是说,创建一个QueryOver.Of<Topic>()
,但我无法弄清楚如何在最后选择程序ID - select只给了我TopicIds,甚至然后查询仍然连接所有三个表。
我不确定MS-SQL的查询优化器是否会避免无用的连接,但如果我们不必依赖它就会很好。
现在虽然如此,但是希望其他人比我试图解决这个问题更少的头痛。