NHibernate可以查询特定的孩子而无需延迟加载整个集合吗?

时间:2013-01-15 09:28:29

标签: nhibernate linq-to-nhibernate

当我有一个具有一对多子集合的实体对象,并且我需要查询特定的子对象时,是否有一个功能或一些聪明的模式我还没有提出以避免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
        }
    }
}

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

3 个答案:

答案 0 :(得分:4)

简答:不。更长的答案:你可以用一些手法做到这一点。

Rippo上面的回答显示了你如何以'正确'的NHibernate方式做到这一点(无论是Linq还是QueryOver或HQL都没关系 - 关键是你必须走出父母 - &gt;子关系去做一个问题)。你可以更进一步,在幕墙背后掩饰这一点。但要这样做,您必须完全删除映射的关系,并始终用查询替换它。你要取出父母 - &gt;儿童绘图,但留下孩子 - &gt;父映射完好无损;然后在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(围绕这些部分)编写了一个精确的尖峰,为NHibernate集合添加了IQueryable支持,所以你可以做到

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模型是会话 - &gt;查询,这是99%的人使用。

答案 1 :(得分: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);

答案 2 :(得分: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.
...
相关问题