2012-12-11 40 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