NHibernate能否在不延迟加载整个集合的情况下查询特定子级?

NHibernate能否在不延迟加载整个集合的情况下查询特定子级?,nhibernate,linq-to-nhibernate,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 C

当我有一个具有一对多子集合的实体对象,并且我需要查询一个特定的子对象时,是否有一个功能或一些巧妙的模式我还没有想出,以避免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
        }
    }
}
类父类
{
公共虚拟int Id{get;proteced set;}//生成的PK
公共虚拟IEnumerable子项{get;proteced set;}
}
班童
{
公共虚拟int Id{get;protected set;}//生成的PK
公共虚拟字符串名称{get;protected set;}
公共虚拟父级{get;protected set;}
}
//用Fluent映射
班级服务
{
私有只读ISessionFactory sessionFactory;
公共服务(ISessionFactory sessionFactory)
{
this.sessionFactory=sessionFactory;
}
void DoSomethingWithChildrenNamedBob(int parentId)
{
使用(var session=sessionFactory.OpenSession())
{
var parent=session.Get(parentId);
//将导致所有孩子的懒惰!
var childrenNamedBob=parent.Children.Where(c=>c.Name==“Bob”);
//对孩子们做点什么
}
}
}

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

几周前,我在NHusers上问了同样的问题,但没有得到答案,所以我怀疑答案是
,您将始终获得所有父对象和子对象,然后执行内存中的筛选器。在许多情况下,这可能是正确的观察方式

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

var childrenNamedBob = session.Query<Children>()
  .Where(w => w.Parent.Id == parentId && w.Name == "Bob");
或者正如你正确指出的那样:-

var parent = session.Get<Parent>(parentId);
var parent=session.Get(parentId);

简短回答:不再。回答:你可以用一些花招让它这样做

上面Rippo的回答显示了如何以“正确”的NHibernate方式进行查询(无论是使用Linq、QueryOver还是HQL都不重要,关键是您必须跳出父->子关系才能进行查询)。你可以更进一步,把它伪装成一个外表。但要做到这一点,您必须完全删除映射关系,并始终用查询替换它。您可以取出父->子映射,但保持子->父映射不变;然后在父对象上重新写入属性,如下所示:

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);
    }
}
您可以这样做并保留映射的唯一方法是修改NHibernate的集合对象(或注入您自己的)。迭戈·米耶尔松(Diego Mijelshon)(他在这些地方工作)写了一篇文章,为NHibernate系列增加了IQueryable支持,这样你就可以做到了

IEnumerable<Child> childrenNamedBob = parent.Children.AsQueryable().Where(c => c.Name == "Bob");
IEnumerable childrenNamedBob=parent.Children.AsQueryable()。其中(c=>c.Name==“Bob”);
但从我所看到的,这从来没有进一步,也没有明显的计划,以增加这一能力的NH。我已经运行了Diego的代码,它确实有效,但显然它不是生产质量,也没有经过测试,而且我认为它从未正式“发布”,即使是作为一个私人补丁

以下是NH问题追踪器讨论的链接:


我相信NH应该支持这种开箱即用的方式,因为对于大多数.NET开发人员来说,这是一种自然的方式,他们想要与几乎所有可枚举的东西进行交互,现在我们有了Linq,并且不能在没有将无限集合加载到RAM中的副作用的情况下进行交互。但是传统的NH模型是session->query,99%的人使用它。

您现在可以直接使用NHibernate 5,而无需特定代码


你有没有试着把你要找的孩子装进去,把父母也装进去?当然,在这种特殊情况下我可以这样做。因此,正如我所说,这不是最好的代码示例。但这并不能回答我的一般问题,我也很害怕。我可能不得不处理这件事。除非接下来几天出现其他问题,否则我会将其标记为已接受。顺便说一句,如果我需要对父对象的引用,我仍然会使用session.Get(parentId)显式查询它,即使在使用您的查询查询子对象之后也是如此。首先,它的效率至少和childrenNamedBob.First()一样高。多亏了会话缓存,Parent;其次,我不必检查/假设子查询是否有结果。是的,它将发出相同的查询,并且您的版本很可能更有意义。感谢您的全面回答。我非常同意NH应该开箱即用地支持这一点,遗憾的是它没有。因为我肯定不想让我的实体知道NH,所以我必须处理它,并尽可能质疑Rippo指出的方式。我同意,这是一种权衡。持久性不可知实体是一件好事。AOP方法可能值得考虑——这是我以前考虑过的。是的,这可能会奏效,但这增加了另一层复杂性,NH已经足够复杂了。我觉得这不值得。@chris-我知道这是一年多以后的事了,但是尼尔说的扩展是。我目前有一个fork,它有一些重构来防止抛出异常(当我开始使用它时)。您可以在Oo上安装NuGet或my fork的原始版本,很好。我得试试看!
IEnumerable<Child> childrenNamedBob = parent.Children.Where(c => c.Name == "Bob");
IEnumerable<Child> childrenNamedBob = parent.Children.AsQueryable().Where(c => c.Name == "Bob");
Build 5.0.0
=============================

** Highlights
...
    * Entities collections can be queried with .AsQueryable() Linq extension without being fully loaded.
...