了解NHibernate + Linq + Future

时间:2011-02-10 10:07:48

标签: linq nhibernate linq-to-nhibernate

我想在编写Linq查询时使用NHibernate进行分页。做这样的事很容易:

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

但是有了这个,我没有得到任何关于物品总数的信息。如果我只是做一个简单的.Count(),那将生成一个对数据库的新调用。

我发现this answer通过使用未来来解决它。但它使用Criteria。我怎么能用Linq做到这一点?

4 个答案:

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