Коллекция NHibernate IQueryable как свойство root - PullRequest
14 голосов
/ 05 мая 2010

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

Например:

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

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

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

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

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

Ответы [ 3 ]

12 голосов
/ 15 мая 2010

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

Вы можете отфильтровать коллекции от объекта, используя 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 для 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);

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

3 голосов
/ 06 мая 2010

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

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

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

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

, например

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

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

1 голос
/ 05 мая 2010

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

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