使用 NHibernate 在最后一个节点中过滤 属性 上的嵌套模型

Filter nested models on property in last node with NHibernate

我正在使用带有代码映射的 NHibernate。

我有三个模型:解决方案、安装和系统。它们之间是一对多的关系。这样每个解决方案都有一个安装列表,每个安装都有一个系统列表。

每个系统都有一个属性 "Type",可以是“1”或“0”。

我正在尝试在解决方案存储库中编写一个方法,该方法将 return 所有解决方案,其安装仅包含“1”类型的系统。

我已经尝试了 SystemMap 中的 Where 关键字,但使用和不使用它得到的结果都是一样的。然后我用 QueryOver(???) 尝试了几个不同的实验但没有成功。

我该如何过滤最后一个节点中的信息?

感谢您的回答,我已经完成了以下实施,但它产生了大量的系统和解决方案。也许我做错了什么?

地图如下:

    public SAPSolutionMap()
    {
        Id(t => t.YPID);

        Property(e => e.ShortName);
        Property(e => e.FullName);

        Bag(x => x.SapInstallations, colmap =>
        {
            colmap.Table("SAPInstallation");
            colmap.Key(x => x.Column("Solution"));
            colmap.Inverse(true);
            colmap.Lazy(CollectionLazy.NoLazy);
            colmap.Fetch(CollectionFetchMode.Join);
            colmap.Cascade(Cascade.None);
        }, map => map.OneToMany(m => m.Class(typeof(SAPInstallation))));
    }

    public SAPInstallationMap()
    {
        Id(t => t.InstallationNumber);

        Bag(x => x.SapSystems, colmap =>
        {
            colmap.Table("sapgui");
            colmap.Key(x => x.Column("Installation"));
            colmap.Inverse(true);
            colmap.Lazy(CollectionLazy.NoLazy);
            colmap.Cascade(Cascade.None);
            colmap.Fetch(CollectionFetchMode.Join);
            //colmap.Where("Type = 1");
        }, map => map.OneToMany(m => m.Class(typeof(SAPSystem))));

        ManyToOne(x => x.SapSolution, map =>
        {
            map.Column("Solution");
            map.NotNullable(true);
            map.Cascade(Cascade.None);
            map.Class(typeof(SAPSolution));
        });
    }

    public SAPSystemMap()
    {
        Id(t => t.ID, t => t.Generator(Generators.Identity));
        Property(e => e.Type);
        Property(e => e.ExplanationText);

        ManyToOne(x => x.SapInstallation, map =>
        {
            map.Column("Installation");
            map.NotNullable(true);
            map.Cascade(Cascade.None);
            map.Class(typeof(SAPInstallation));
        });
    }

查询:

    public IList<SAPSolution> GetProductionSystems()
    {
        SAPSystem syst = null;
        SAPInstallation installation = null;
        var subquery = QueryOver.Of(() => syst)
            .JoinQueryOver(x => x.SapInstallation, () => installation)
            .Where(() => syst.Type == 1)
            .Select(x => installation.SapSolution.YPID);

        // main Query 
        var query = Session.QueryOver<SAPSolution>()
            .WithSubquery
                .WhereProperty(root => root.YPID)
            .In(subquery);

        return query.List<SAPSolution>();
    } 

谢谢!

一般的解决方案应该是:

// this is a subquery (SELECT ....
System syst = null;
Installation installation = null;
var subquery = QueryOver.Of(() => syst)
    .JoinQueryOver(x => x.Installation, () => installation)
    .Where(() => syst.Type == 1)
    .Select(x => installation.Solution.ID)
;

// main Query 
var query = session.QueryOver<Solution>()    
    .WithSubquery
        .WhereProperty(root => root.ID)
    .In(subquery)
    ;

var list = query
   .Take(10)
   .Skip(10)
   .List<Solution>();

我们可以看到,解决方案、安装和系统

  • System 有 属性 Installation (many-to-one)
  • Installation 有 属性 Solution (many-to-one)

这是可以预料的,因为它与一对多并行(它是反向映射)

因此,然后我们创建 subquery,其中 returns 只是属于具有搜索类型的系统的解决方案 ID。

主查询是扁平的(最大的好处),我们可以在其上使用分页。

即使只有一种方法(一对多),我们也能做到。但这会生成更复杂的 SQL 查询......并且没有意义。在 C# 中,我们可以同时拥有这两种关系...

延长:

你做得很好。您的映射和查询真的很酷。但是有一个很大的问题:LAZY 是我们 should/MUST 使用的。检查这个:

NHibernate is lazy, just live with it, 来自 Ayende

因此,我们的集合不能通过 JOIN 获取,因为这会使结果成倍增加(10 个解决方案 * 100 个安装 * 10 个系统 == 10000 个结果)

Bag(x => x.SapSystems, colmap =>
{ 
    ...
    // THIS IS not good way
    colmap.Lazy(CollectionLazy.NoLazy);
    colmap.Fetch(CollectionFetchMode.Join);

我们应该尽可能使用LAZY。为了避免后面的 1 + N 问题,我们可以使用批量获取(例如检查这个)

  • How to Eager Load Associations without duplication in NHibernate?

所以,我们的集合应该这样映射:

Bag(x => x.SapSystems, colmap =>
{ 
    ...
    // THIS IS not good way
    colmap.Lazy(CollectionLazy.Lazy);
    colmap.BatchSize(100);

使用此设置,查询将真正仅使用根对象,相关集合将非常有效地加载