NHibernate QueryOver子查询

Car*_*ema 9 nhibernate queryover

我看过类似的问题,但找不到简单的解释.我本可以错过它,但我保证我看了.实际上我甚至找不到一篇文章,除了一篇博文文章,它快速掩盖了一切,并假设你熟悉其他形式的NH.

鉴于之间的多对多,Program并且Topic后者处于层次结构中Topics,我想要检索Programs给定的所有内容Topic,可能包括其子主题.由于程序可能列在给定父主题的多个子主题下,我需要使用子查询或处理必须使用distinct(并且简单的方法TransformUsing(Transformers.DistinctRootEntity)不起作用).

原始SQL应该是这样的

SELECT ProgramId, Title, bar, baz, foo FROM Programs 
WHERE ProgramId IN 
 (SELECT ProgramId from Program_Topics WHERE TopicId IN (1, 2, ...))
Run Code Online (Sandbox Code Playgroud)

结果将转换为模型类型以传输到视图.我最初的尝试是这样的:

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>();    
Run Code Online (Sandbox Code Playgroud)

显然这会运行一个连接而不是一个子查询,但我找不到像这样多对多的子查询的例子.

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; }
    ...
}
Run Code Online (Sandbox Code Playgroud)

Rip*_*ppo 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 };
Run Code Online (Sandbox Code Playgroud)

现在,这个详细的查询: -

var idSubQuery = QueryOver.Of<CmsRegionContent>()
  .WhereRestrictionOn(w => w.Id).IsIn(ids)
  .Select(Projections.Distinct(Projections.Property<CmsPage>(s => s.Id)));
Run Code Online (Sandbox Code Playgroud)

最后一点是将它们放在一起: -

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>();
Run Code Online (Sandbox Code Playgroud)

这将创建以下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 */)
    )
Run Code Online (Sandbox Code Playgroud)

希望您能够使用您的类/属性名称进行此操作.


Car*_*ema 5

好吧,再讨论一下,虽然我不喜欢结果的一部分,但它确实有效:

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>();
Run Code Online (Sandbox Code Playgroud)

这产生

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, ...
            )
    ) 
Run Code Online (Sandbox Code Playgroud)

这可能是 NH 的限制,但不需要在子查询中加入Programs表。我试图从另一个方向写这个——也就是说,创建一个QueryOver.Of<Topic>(),但我无法弄清楚如何在最后选择程序 ID——select 只给了我 TopicIds,即使这样查询仍然是加入所有三个表。

我不确定 MS-SQL 的查询优化器是否会避免无用的连接,但如果我们不必依赖它就好了。

不过,就目前而言,这是有效的,希望其他人比我试图解决这个问题时更头疼。