2011-02-10 74 views
18

我想在編寫Linq查詢時使用NHibernate進行分頁。這很容易做到這樣的事情:使用NHibernate + Linq +獲取計數Future

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

但與此我沒有得到任何關於項目總數的信息。如果我只是做一個簡單的.Count(),那會生成一個新的數據庫調用。

我發現this answer它解決了它通過使用未來。但它使用Criteria。我怎樣才能與Linq做到這一點?

+1

使用Session.QueryOver代替 - 這樣可以節省IntelliSense和 'compileability',並有一個SelectCount方法。如果您需要我可以提供使用它的詳細示例 – Genius 2011-02-10 12:03:15

+0

是的,QueryOver似乎也可以正常工作! – Allrameest 2011-02-10 12:56:19

+1

(我在我的解決方案中發現了一個錯誤,並將其刪除以避免混淆,我將很快發佈固定版本) – 2011-02-11 11:26:11

回答

30

在LINQ中使用Futures的困難在於像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關於這個問題,一個更好的解決方案。

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

這不是他需要的。 – 2011-02-10 13:35:58

1
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

以下博客文章實現了與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; 

應用更改,你可以以這種方式使用闕查詢後:

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