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