NHibernate IQueryable集合作为root的属性

时间:2010-05-05 14:28:42

标签: c# nhibernate fluent-nhibernate domain-driven-design

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

例如:

I have a Shelf object that has Books.

// Now
public class Shelf 
{
    public ICollection<Book> Books {get; set;}
}

// Want 
public class Shelf 
{
   public IQueryable<Book> 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时当前发生的事情)。

这背后的原因是我的收藏可能很庞大,成千上万的记录,并且所有查询都可能会破坏我的数据库。

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

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

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

3 个答案:

答案 0 :(得分:12)

我一直在尝试为类似问题提出解决方案。

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

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

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

但是有一个问题:

  1. 执行代码的消费者 需要访问 ISession.CreateFilter,或者你需要 在你的上创建一个方法 接收属性的存储库, 查询和查询参数 (以及任何传呼或其他 信息)。不是最性感的 这个星球上的东西。
  2. 这不是你想要的LINQ。
  3. 不幸的是,我认为没有任何办法可以通过NHibernate获得你想要的东西。如果你想尝试的话,你可以假装它,但它们似乎对我不利:

    添加一个方法或属性,该方法或属性为此架子返回LINQ到NHibernate IQueryable

    public IQueryable<Book> FindBooks() {
      return Resolver.Get<ISession>().Linq<Book>().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<Book> FindBooks() {
      return Resolver.Get<IRepository<Book>>().CreateQuery().Where(b => b.Shelf == this);
    }
    

    还是很糟糕。

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

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

答案 1 :(得分:3)

我倾向于这样想:

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

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

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

e.g。

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

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

答案 2 :(得分:1)

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

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