NHibernate 3 paging и определение общего количества строк

Я где-то читал (не могу вспомнить, где и как), что NHibernate 3 позволяет определить общее количество записей при выполнении постраничного запроса (в одном запросе базы данных). Правильно ли это?

У меня есть этот код:

public IEnumerable<X> GetOrganisms(int PageSize, int Page, out int total)
{
    var query = (from e in Session.Query<X>() select e).AsQueryable();

    return query.Skip((Page - 1) * PageSize).Take(PageSize).ToList();
}

и хотел бы как можно более эффективно инициализировать "total".

Спасибо.

Christian

PS:

Потенциальное "решение"?:

Total = (int) Session.CreateCriteria<X>()
.SetProjection(Projections.RowCount())
.FutureValue<Int32>().Value;

var query = (from e in Session.Query<X>() select e).AsQueryable();

return query.Skip((Page - 1) * PageSize).Take(PageSize).ToList();

Ответ 1

Ваше потенциальное решение будет обрабатываться в одной транзакции, но будет два вызова db. Если у вас должен быть только один вызов db, вы должны использовать многоэкранный/будущий запрос, как предлагалось. Дополнительную информацию о синтаксисе будущего см. В этом сообщении: http://ayende.com/blog/3979/nhibernate-futures.

Вот несколько способов выполнить ваш сценарий...

QueryOver (вызовы 2 дБ):
var query = session.QueryOver<Organism>();
var result = query
    .Skip((Page - 1) * PageSize)
    .Take(PageSize)
    .List();
var rowcount = query.RowCount();

С образцом набора из 100 организмов и запросом для организмов 11-20, вот два запроса, отправленные в db:

SELECT TOP (@p0) Id0_0_, Title0_0_ FROM (SELECT this_.Id as Id0_0_, this_.Title as Title0_0_, ROW_NUMBER() OVER(ORDER BY CURRENT_TIMESTAMP) as __hibernate_sort_row FROM Organism this_) as query WHERE query.__hibernate_sort_row > @p1 ORDER BY query.__hibernate_sort_row;@p0 = 10 [Type: Int32 (0)], @p1 = 10 [Type: Int32 (0)]
SELECT count(*) as y0_ FROM Organism this_
QueryOver (вызов 1 дБ с будущим):
var query = session.QueryOver<Organism>()
    .Skip((Page - 1) * PageSize)
    .Take(PageSize)
    .Future<Organism>();
var result = query.ToList();
var rowcount = session.QueryOver<Organism>()
    .Select(Projections.Count(Projections.Id()))
    .FutureValue<int>().Value;

Запрос для того же набора данных, что и раньше, это запрос, который сгенерирован:

SELECT TOP (@p0) Id0_0_, Title0_0_ FROM (SELECT this_.Id as Id0_0_, this_.Title as Title0_0_, ROW_NUMBER() OVER(ORDER BY CURRENT_TIMESTAMP) as __hibernate_sort_row FROM Organism this_) as query WHERE query.__hibernate_sort_row > @p1 ORDER BY query.__hibernate_sort_row;SELECT count(this_.Id) as y0_ FROM Organism this_;;@p0 = 10 [Type: Int32 (0)], @p1 = 10 [Type: Int32 (0)]
Критерии (вызов 1 дБ с будущим):
var criteria = session.CreateCriteria<Organism>()
    .SetFirstResult((Page - 1) * PageSize)
    .SetMaxResults(PageSize)
    .Future<Organism>();
var countCriteria = session.CreateCriteria<Organism>()
    .SetProjection(Projections.Count(Projections.Id()))
    .FutureValue<int>().Value;

Опять же, запрос для одного и того же набора данных, критерии с будущими результатами в том же запросе:

SELECT TOP (@p0) Id0_0_, Title0_0_ FROM (SELECT this_.Id as Id0_0_, this_.Title as Title0_0_, ROW_NUMBER() OVER(ORDER BY CURRENT_TIMESTAMP) as __hibernate_sort_row FROM Organism this_) as query WHERE query.__hibernate_sort_row > @p1 ORDER BY query.__hibernate_sort_row;SELECT count(this_.Id) as y0_ FROM Organism this_;;@p0 = 10 [Type: Int32 (0)], @p1 = 10 [Type: Int32 (0)]

Обратите внимание, что все три стиля запроса приводят к тем же самым точным запросам. Будущий синтаксис просто позволяет NHibernate выполнять один вызов базы данных, а не два.

Если вы используете NHibernate 3, я считаю, что самый элегантный способ справиться с этим - использовать новый синтаксис QueryOver. (Вы использовали старый синтаксис NHibernate.Linq в предлагаемом решении. Вам лучше изучить синтаксис QueryOver.)

Ответ 2

У меня недостаточно репутации, чтобы комментировать решение CodeProgression выше... но правильный вызов ONE DB с использованием QueryOver w/Future < > is:

var query = session.QueryOver<Organism>()
    .Skip((Page - 1) * PageSize)
    .Take(PageSize)
    .Future<Organism>();
// var result = query.ToList();
var rowcount = session.QueryOver<Organism>()
    .Select(Projections.Count(Projections.Id()))
    .FutureValue<int>().Value;
var result = query.ToList();
int iRowCount = rowcount.Value();

После выполнения .ToList() - он попадет в базу данных. Таким образом, вам придется ударить базу данных еще раз, чтобы получить rowCount... Что побеждает цель Future < > . Сделайте свой список ToList() ПОСЛЕ того, как вы выполнили все ваши запросы .Future < > .

Ответ 3

Я не думаю, что nhibernate "реализует" значение любого выполняемого запроса, поэтому определите общее количество строк, которые не заданы стандартным образом.

Самый эффективный способ получить количество строк - это фьючерсы или IMultiQuery (чтобы получить все результаты в одном округлении до базы данных)

nhibernate-futures