Коллекция NHibernate IQueryable как свойство root

У меня есть объект root, у которого есть свойство, которое является коллекцией.

Например:

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).

Обоснование этого заключается в том, что моя коллекция может быть огромной, десятки тысяч записей, и получить весь запрос может bash моя база данных.

Я хотел бы сделать это неявно, чтобы, когда NHibernate увидит IQueryable в моем классе, он знает, что делать.

Я посмотрел на поставщика NHibernate LINQ, и в настоящее время я принимаю решение взять большие коллекции и разбить их на свой собственный репозиторий, чтобы я мог делать явные вызовы для фильтрации и подкачки.

LINQ To SQL предлагает нечто похожее на то, о чем я говорю.

Ответы

Ответ 1

Я пытаюсь найти решение для аналогичной проблемы.

Вы можете отфильтровывать коллекции с объекта с помощью 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>();

Однако есть проблема с этим:

  • Пользователь, выполняющий код нуждается в доступе ISession.CreateFilter, или вам нужно для создания метода на вашем репозиторий, который принимает свойство, запрос и ваши аргументы запроса (а также любой пейджинг или другое Информация). Не самый сексуальный вещь на планете.
  • Это не 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 над коллекцией для вас, кажется, что вам лучше просто запрашивать ваш репозиторий книг, как это уже было предложено, даже если это не выглядит как сексуальное или оптимальное.

Ответ 2

Я склонен думать так:

Совокупные корни - это границы согласованности, поэтому, если полке необходимо применять какие-то политики согласованности в содержащихся в ней книгах, тогда это должен быть совокупный корень. И в этом случае он должен содержать набор/собрание книг.

Если вам не нужно принудительно выполнять согласованность с полки в книгах, я бы решил удалить свойство set/collection и переместить эти запросы в репозиторий.

Кроме того, поскольку разбиение на страницы и фильтрация, скорее всего, не имеет ничего общего с логикой вашего домена, это скорее всего для презентации. Тогда я подумал бы сделать для него особый взгляд, вместо того, чтобы добавлять реквизиты презентации в свои репозитории.

например.

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

Такой запрос может возвращать проекции и/или оптимизироваться как простой SQL и т.д. Они, скорее всего, предназначены для определенного вида или отчета в вашем графическом интерфейсе. Таким образом, ваш домен будет сосредоточен только на концепциях домена.

Ответ 3

Возможно, вам следует дать Nhibernate Linq. Это позволяет использовать IQueryable и делать такие вещи, как:

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