Набор NHibernate IQueryable как свойство корня

У меня есть корневой объект, который имеет свойство, которое является набором.

Например:

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 К SQL предлагает что-то подобное тому, о чем я говорю.

14
задан Peter Mortensen 25 May 2010 в 17:16
поделиться

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 над коллекцией для вас, похоже, что вам лучше просто запросить ваше хранилище книг обычным способом, как уже было предложено, даже если это кажется не таким сексуальным или оптимальным.

12
ответ дан 1 December 2019 в 13:58
поделиться

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

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

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

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

например

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

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

3
ответ дан 1 December 2019 в 13:58
поделиться

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

Session.Linq<Book>().Where(b => b.Name == "The Great Gatsby");
1
ответ дан 1 December 2019 в 13:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: