Подтвердить что ты не робот

Получение счета с NHibernate + Linq + Future

Я хочу выполнить подкачку с помощью NHibernate при написании запроса Linq. Легко сделать что-то вроде этого:

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

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

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

4b9b3361

Ответ 1

Трудность использования фьючерсов с LINQ заключается в том, что операции, подобные Count, выполняются немедленно.

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

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

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

//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): я написал сообщение в блоге об этой проблеме и гораздо лучшем решении.

Ответ 2

В следующем блоге есть реализация 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;

После применения изменения вы можете использовать запросы que таким образом:

var query = session.Query<Foo>();
var futureCount = query.ToFutureValue(x => x.Count());
var page = query.Skip(pageIndex * pageSize).Take(pageSize).ToFuture();

Ответ 3

var query = Session.QueryOver<Payment>()
    .OrderByDescending(payment => payment.Created)
    .Skip((page -1 ) * pageSize)
    .Take(pageSize)

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

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

var result = query.Future();

var asArray = result.ToArray();
var count = rowCount.Value();

Ответ 4

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

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();