Получение отсчета с NHibernate + Linq + Future - PullRequest
18 голосов
/ 10 февраля 2011

Я хочу делать пейджинг с помощью NHibernate при написании запроса Linq.Это легко сделать примерно так:

return session.Query<Payment>()
    .OrderByDescending(payment => payment.Created)
    .Skip((page - 1)*pageSize)
    .Take(pageSize)
    .ToArray();

Но при этом я не получаю никакой информации об общем количестве предметов.И если я просто сделаю простой .Count (), он сгенерирует новый вызов базы данных.

Я нашел этот ответ , который решил его с помощью будущего.Но он использует критерии.Как я могу сделать это с Linq?

Ответы [ 4 ]

30 голосов
/ 11 февраля 2011

Сложность использования Futures с LINQ заключается в том, что такие операции, как Count, выполняются немедленно.

Как выяснил @vandalo, Count() после ToFuture() фактически запускает счетчик в памяти, что плохо.

Единственный способ получить счетчик в будущем запросе LINQ - это использовать GroupBy в инвариантном поле. Хороший выбор - это то, что уже является частью ваших фильтров (например, свойство IsActive)

Вот пример, предполагающий, что у вас есть такое свойство в Payment:

//Create base query. Filters should be specified here.
var query = session.Query<Payment>().Where(x => x.IsActive == 1);
//Create a sorted, paged, future query,
//that will execute together with other statements
var futureResults = query.OrderByDescending(payment => payment.Created)
                         .Skip((page - 1) * pageSize)
                         .Take(pageSize)
                         .ToFuture();
//Create a Count future query based on the original one.
//The paged query will be sent to the server in the same roundtrip.
var futureCount = query.GroupBy(x => x.IsActive)
                       .Select(x => x.Count())
                       .ToFutureValue();
//Get the results.
var results = futureResults.ToArray();
var count = futureCount.Value;

Конечно, альтернатива состоит в том, чтобы сделать две поездки туда и обратно, что не так уж и плохо. Вы по-прежнему можете повторно использовать исходный IQueryable, что полезно, когда вы хотите выполнить подкачку на более высоком уровне:

//Create base query. Filters should be specified here.
var query = session.Query<Payment>();
//Create a sorted, paged query,
var pagedQuery = query.OrderByDescending(payment => payment.Created)
                      .Skip((page - 1) * pageSize)
                      .Take(pageSize);
//Get the count from the original query
var count = query.Count();
//Get the results.
var results = pagedQuery.ToArray();

Обновление (2011-02-22): я написал сообщение в блоге об этой проблеме и гораздо лучшем решении.

4 голосов
/ 26 сентября 2012

В следующем сообщении блога есть реализация ToFutureValue, которая работает с LINQ.

http://sessionfactory.blogspot.com.br/2011/02/getting-row-count-with-future-linq.html

В следующей строке есть небольшая ошибка, которую необходимо изменить из этого.

var provider = (NhQueryProvider)source.Provider;

К этому:

var provider = (INhQueryProvider)source.Provider;

После применения изменения вы можете использовать запросы к очереди следующим образом:

var query = session.Query<Foo>();
var futureCount = query.ToFutureValue(x => x.Count());
var page = query.Skip(pageIndex * pageSize).Take(pageSize).ToFuture();
1 голос
/ 05 мая 2011
var query = Session.QueryOver<Payment>()
    .OrderByDescending(payment => payment.Created)
    .Skip((page -1 ) * pageSize)
    .Take(pageSize)

Это то, что я только что обнаружил, что Linq to NH отлично справляется, ToRowCountQuery удаляет take / skip из запроса и делает счетчик будущих строк.

var rowCount = query.ToRowCountQuery().FutureValue<int>();

var result = query.Future();

var asArray = result.ToArray();
var count = rowCount.Value();
0 голосов
/ 10 февраля 2011

Хорошо, похоже, это должно работать в вашем случае, но я не проверял:

return session.QueryOver<Payment>()
  .Skip((page - 1) * pageSize)
  .Take(pageSize)
  .SelectList(r => r.SelectCount(f => f.Id))
  .List<object[]>().First();

Сначала проверьте перед голосованием;)

UPD: извините, насколько я понимаю, вам нужно получить количество всех предметов. Затем вам нужно выполнить запрос без подкачки:

return session.QueryOver<Payment>()
  .SelectList(r => r.SelectCount(f => f.Id))
  .List<object[]>().First();
...