代码之家  ›  专栏  ›  技术社区  ›  Khalid Abuhakmeh

根的特性

  •  14
  • Khalid Abuhakmeh  · 技术社区  · 14 年前

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

    例如:

    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执行,而不是get all来加载整个集合,然后在内存中解析它(这是当前使用i collection时发生的情况)。

    这背后的原因是,我的集合可能是巨大的,数以万计的记录,一个get-all查询可能会破坏我的数据库。

    我想隐式地这样做,以便当nhibernate在我的类上看到一个iqueryable时,它知道该怎么做。

    我已经研究过nhibernate的linq提供者,目前我正在做一个决定,把大的集合拆分成它们自己的存储库,这样我就可以显式地调用筛选和分页。

    linq to sql提供了与我所说的类似的功能。

    3 回复  |  直到 14 年前
        1
  •  12
  •   moribvndvs    14 年前

    我一直在想一个解决类似问题的办法。

    可以使用 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. 不是你想要的林肯。

    不幸的是,我不认为有任何方法可以得到你想要的与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之前,您最好像已经建议的那样正常地查询您的图书存储库,即使它看起来没有那么性感或最佳。

        2
  •  3
  •   Roger Johansson    14 年前

    我倾向于这样想:

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

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

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

    例如

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

    这种查询可以返回投影和/或作为普通sql等进行优化。 它们很可能是特定于gui中的某个视图或报表的。 这样你的领域将只关注领域概念。

        3
  •  1
  •   Jonny Cundall    14 年前

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

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