2

当我有一个带有一对多子集合的实体对象,并且我需要查询一个特定的子对象时,是否有一个我还没有想出的功能或一些聪明的模式来避免 NHibernate 获取整个儿童收藏?

例子:

class Parent 
{
    public virtual int Id { get; proteced set; } // generated PK
    public virtual IEnumerable<Child> Children { get; proteced set; }
}

class Child
{
    public virtual int Id { get; protected set; } // generated PK
    public virtual string Name { get; protected set; }
    public virtual Parent Parent { get; protected set; }
}

// mapped with Fluent

class Service
{

    private readonly ISessionFactory sessionFactory;

    public Service(ISessionFactory sessionFactory)
    {
        this.sessionFactory = sessionFactory;
    }

    void DoSomethingWithChildrenNamedBob(int parentId)
    {
        using(var session = sessionFactory.OpenSession())
        {
            var parent = session.Get<Parent>(parentId);
            // Will cause lazy fetch of all children!
            var childrenNamedBob = parent.Children.Where(c => c.Name == "Bob"); 
            // do something with the children
        }
    }
}

我知道这不是最好的例子,因为在这种情况下,可能只是直接查询子实体,但我遇到过我已经有一个父对象并且需要遍历它的特定子树的情况。

4

3 回答 3

4

简短的回答:没有。更长的答案:您可以通过一些技巧来做到这一点。

上面 Rippo 的回答显示了您将如何以“正确的”NHibernate 方式执行此操作(无论是使用 Linq 还是 QueryOver 或 HQL 都无关紧要 - 关键是您必须走出父级 -> 子级关系才能进行查询)。您可以更进一步,将其隐藏在外观后面。但要这样做,您必须完全删除映射关系并始终将其替换为查询。您将取出 Parent -> Children 映射,但保留 Child -> Parent 映射不变;然后重写 Parent 上的属性,如下所示:

public virtual IQueryable<Child> Children
{
    get
    {
        // somehow get a reference to the ISession (I use ambient context), then
        return session.Query<Child>().Where(c => c.Parent == this);
    }
}

现在,当您使用时,Parent.Children您会返回一个可查询的集合,因此您可以编写

IEnumerable<Child> childrenNamedBob = parent.Children.Where(c => c.Name == "Bob");

您可以这样做并保留映射的唯一方法是修改 NHibernate 的集合对象(或注入您自己的)。Diego Mijelshon(在这些部分周围)写了一个确切的峰值,将 IQueryable 支持添加到 NHibernate 集合中,这样你就可以做到

IEnumerable<Child> childrenNamedBob = parent.Children.AsQueryable().Where(c => c.Name == "Bob");

但据我所知,这从未进一步发展,也​​没有明显的计划将这种能力添加到 NH。我已经运行了 Diego 的代码,它确实可以工作,但显然它不是生产质量,也没有经过测试,而且我认为它从未被正式“发布”过,即使是作为一个私人补丁。

这是关于 NH 问题跟踪器的讨论链接:https ://nhibernate.jira.com/browse/NH-2319

我相信 NH 应该开箱即用地支持这一点,因为这是大多数 .NET 开发人员想要与几乎任何可枚举的东西进行交互的自然方式,现在我们有了 Linq,并且如果没有副作用就无法做到这一点将无限集合加载到 RAM 中很糟糕。但是传统的 NH 模型是会话 -> 查询,这是 99% 的人使用的。

于 2013-01-15T11:25:16.050 回答
3

几周前我在 NHusers 上问了同样的问题,但没有得到答案,所以我怀疑答案是you will always get all the parents children and then perform a in-memory filter. 在许多情况下,这可能是看待它的正确方式。

在您的情况下,我会将查询重写为:-

var childrenNamedBob = session.Query<Children>()
  .Where(w => w.Parent.Id == parentId && w.Name == "Bob");

然后简单地获取父母(如果 childrenNamedBob 有结果),您可以调用:-

   var parent = childrenNamedBob.First().Parent;

或者正如您正确指出的那样:-

var parent = session.Get<Parent>(parentId);
于 2013-01-15T10:06:07.380 回答
2

您现在可以直接使用 NHibernate 5 做到这一点,无需特定代码!

https://github.com/nhibernate/nhibernate-core/blob/master/releasenotes.txt

Build 5.0.0
=============================

** Highlights
...
    * Entities collections can be queried with .AsQueryable() Linq extension without being fully loaded.
...
于 2017-11-02T20:36:54.253 回答