【问题标题】:NHibernate IQueryable collection as property of rootNHibernate IQueryable 集合作为根的属性
【发布时间】:2011-02-15 23:24:52
【问题描述】:

我有一个具有集合属性的根对象。

例如:

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 提供的功能与我所说的类似。

【问题讨论】:

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


    【解决方案1】:

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

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

    【讨论】:

    • “我查看了 NHibernates Linq 提供程序,目前我决定采用大型集合并将它们拆分到自己的存储库中,以便我可以明确调用过滤和分页。”
    • @Khalid - 我不知道为什么这对你没有帮助。这正是你所要求的
    • 我的问题是“如何让 NHibernate 映射到 IQueryable 属性?”我已经知道 NHibernate Linq。这是一个我从未问过的问题的答案,这就是我投反对票的原因。
    【解决方案2】:

    我倾向于这样想:

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

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

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

    例如

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

    这样的查询可以返回预测和/或优化为普通 SQL 等。 它们很可能特定于 GUI 中的某个视图或报告。 这样,您的领域将只关注领域概念。

    【讨论】:

      【解决方案3】:

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

      您可以使用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。

      不幸的是,我认为没有任何方法可以通过 NHibernate 开箱即用地获得您想要的东西。如果你想尝试,你可以假装它,但它们似乎对我来说很平淡:

      添加一个方法或属性,在幕后为这个架子返回一个 LINQ to 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 to 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 存储库,即使它看起来并不性感或最佳。

      【讨论】:

      • 感谢您的解释如此透彻。我已经考虑过你所说的事情,我同意你提出的所有观点。想知道 NHibernate 3 是否会开箱即用地支持这一点。
      猜你喜欢
      • 2015-03-21
      • 2012-06-24
      • 2018-02-03
      • 2020-09-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-05
      • 1970-01-01
      相关资源
      最近更新 更多