2012-12-11 77 views
0

我在一個C#項目中的SQLite數據庫上使用NHibernate。我有一個看起來像這樣的通用批量數據處理方法:NHibernate與linq分頁性能

private void DataProcess<Tobj>(int pageSize, Expression<Func<Tobj, bool>> whereClause, 
     Action<Tobj, ISession> dataProcessingCallback) where Tobj : IModelBase 
    { 
     int offset = 0; 
     bool moreToGet = true; 

     while (moreToGet) 
     { 
      DataAccess((ISession session) => 
      { 
       IEnumerable<Tobj> result = session.Query<Tobj>().Where(whereClause); 
       List<Tobj> data = result.Skip(offset) 
        .Take(pageSize) 
        .ToList(); 
       foreach (Tobj item in data) { dataProcessingCallback(item, session); } 

       if (data.Count == pageSize) { offset += pageSize; } 
       else { moreToGet = false; } 
      }); 
     } 
    } 

(在DataAccess方法爲我們提供了我們的會話對象一起工作,處理事務等)

說完看了看四周,這似乎非常類似於大多數其他linq分頁實現。他們通常做一個.Skip()。拿()

我的問題是,對於大型數據集(在測試中,我看着有大約200k行)需要AGES(調試器中大約20秒)執行結果.skip(offset).Take(pageSize).ToList();線。 這是與pageSize = 100和偏移量= 0.

我的理解是,由於推遲執行,SELECT不會發生,直到它需要(在這種情況下.ToList())。此時,它知道它需要哪些行只應選擇相關的100行。

'data'有預期的100行,但好像系統已經從DB獲取了所有200k奇數行,然後在代碼中完成了分頁。

我對LINQ/NHibernate的理解是否正確? 如果是這樣,我想我需要做這樣的事情與標準API:NHibernate Paging performance (Better option)

回答

1

看看下面的一行:

IEnumerable<Tobj> result = session.Query<Tobj>().Where(whereClause); 

您使用的是IEnumerable<Tobj>場,因此,當你調用Skip ,您正在調用Enumerable.Skip方法。我相信你想要的是Queryable.Skip方法。

嘗試下面的代碼行:

IQueryable<Tobj> result = session.Query<Tobj>().Where(whereClause); 

這將確保調用正確的擴展方法。

說明

Enumerable.Skip方法將循環遍歷源IEnumerable<Tobj>,然後才返回N個元素之後的結果的每個實體。這將迫使NHibernate將所有數據加載到內存中,因爲NHibernate只知道數據是必需的。

Queryable.Skip方法將改爲構建一個表達式樹,以便NHibernate在稍後訪問數據時可以處理它。那時NHibernate知道跳過前N個記錄。

儘管如此,我不確定LINQ提供者目前是否支持SkipTake方法。

+0

Ahhhhhh!謝謝!這很有道理。我做了改變,現在一切都運行完美。 :) – Steve