我想在编写Linq查询时使用NHibernate进行分页。做这样的事很容易:
return session.Query<Payment>()
.OrderByDescending(payment => payment.Created)
.Skip((page - 1)*pageSize)
.Take(pageSize)
.ToArray();
但是有了这个,我没有得到任何关于物品总数的信息。如果我只是做一个简单的.Count(),那将生成一个对数据库的新调用。
我发现this answer通过使用未来来解决它。但它使用Criteria。我怎么能用Linq做到这一点?
答案 0 :(得分:30)
将Futures与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):我写了一篇关于这个问题的blog post和一个更好的解决方案。
答案 1 :(得分:4)
以下博客文章中有一个与LINQ一起使用的ToFutureValue实现。
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();
答案 2 :(得分:1)
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();
答案 3 :(得分:-6)
好吧,它似乎应该适合你的情况,但我没有测试过:
return session.QueryOver<Payment>()
.Skip((page - 1) * pageSize)
.Take(pageSize)
.SelectList(r => r.SelectCount(f => f.Id))
.List<object[]>().First();
在upvoting之前先测试;)
UPD:抱歉,我现在了解你,你需要获得所有物品的数量。然后你需要在没有分页的情况下运行查询:
return session.QueryOver<Payment>()
.SelectList(r => r.SelectCount(f => f.Id))
.List<object[]>().First();