NHibernate IQueryable集合作为root的属性

我有一个根对象,其属性是一个集合。

例如:

I have a Shelf object that has Books. // Now public class Shelf { public ICollection Books {get; set;} } // Want public class Shelf { public IQueryable Books {get;set;} } 

我想要完成的是返回一个IQueryable的集合,这样我就可以直接从父节点运行分页和过滤集合。

 var shelf = shelfRepository.Get(1); var filtered = from book in shelf.Books where book.Name == "The Great Gatsby" select book; 

我希望NHibernate专门执行该查询,而不是全部加载整个集合然后在内存中解析它(这是当我使用ICollection时当前发生的事情)。

这背后的原因是我的collections可能是巨大的,成千上万的记录,并且所有查询都可以打击我的数据库。

我想隐式地这样做,以便当NHibernate在我的课上看到IQueryable时它知道该怎么做。

我查看了NHibernate的LINQ提供程序,目前我正在决定采用大型集合并将它们拆分到自己的存储库中,以便我可以显式调用过滤和分页。

LINQ To SQL提供了类似于我所说的内容。

我一直在努力想出一个类似问题的解决方案。

您可以使用ISession.FilterCollection过滤实体的集合。 这会创建一个额外的IQuery,您可以在其中计算,页面,添加条件等。

所以,例如(我在FilterCollection中的查询可能有点偏,但你应该明白):

 ISession session = GetSession(); var shelf = session.Get(id); var books = session.FilterCollection(shelf.Books, "where Name = :title").SetString("title", "The Great Gatsby").List(); 

但是有一个问题:

  1. 执行代码的使用者需要访问ISession.CreateFilter,或者您需要在存储库中创建一个方法,该方法接收属性,查询和查询参数(以及任何分页或其他信息)。 这不是地球上最性感的东西。
  2. 这不是你想要的LINQ。

不幸的是,我认为没有任何方法可以通过NHibernate获得你想要的东西。 你可以假装它,如果你想尝试,但它们似乎对我不利:

添加一个方法或属性,在这个架子下面返回一个LINQ到NHibernate IQueryable

 public IQueryable FindBooks() { return Resolver.Get().Linq().Where(b => b.Shelf == this); } 

有人可能会像这样消费:

 var shelf = ShelfRepo.Get(id); var books = (from book shelf.FindBooks() where book.Title == "The Great Gatsby" select book); 

呸! 您正在通过域模型消除持久性需求! 也许你可以通过让一个存储库发出IQueryable来使它更糟糕,它在运行时实际上是LINQ到NHibernate:

 public IQueryable FindBooks() { return Resolver.Get>().CreateQuery().Where(b => b.Shelf == this); } 

还是很糟糕。

创建自己的自定义集合类型(可能是IQueryable实现),它包装实际书籍的私有字段,并将NHibernate映射到该字段 。 但是,使用ISession.CreateFilter进行此操作可能很困难。 您必须考虑“发现”当前会话,将LINQ表达式转换为您可以在CreateFilter中使用的内容等。此外,您的业务逻辑仍然依赖于NHibernate。

在这一点上,没有什么能真正满足。 直到NHibernate可以为你的集合做LINQ,看起来你最好只是像已经建议的那样正常查询你的Book存储库,即使它看起来不那么性感或最佳。

我倾向于这样想:

聚合根是一致性的边界,因此如果架子需要在它包含的书籍上强制执行某种一致性策略,那么它应该是聚合根。 在这种情况下,它应该拥有一套书籍。

如果您不需要以任何方式强化从书架到书籍的一致性,那么我会考虑删除set / collection属性并将这些查询移到存储库中。

此外,由于分页和过滤很可能与您的域逻辑没有任何关系,因此最有可能进行演示。 然后我会考虑为它制作一些特殊的视图,而不是向我的存储库添加演示设备。

例如

 var result = Queries.FindBooksByShelf(shelfId,pageSize); 

此类查询可以返回投影和/或优化为纯SQL等。它们很可能特定于GUI中的某个视图或报表。 这样,您的域名将仅关注域概念。

也许你应该试试Nhibernate Linq 。 它允许您使用IQueryable并执行以下操作:

 Session.Linq().Where(b => b.Name == "The Great Gatsby");