NHibernate具有linq分页性能

时间:2012-12-11 13:00:39

标签: c# performance sqlite nhibernate

我在c#项目中的SQLite数据库上使用NHibernate。我有一个通用的批量数据处理方法,如下所示:

    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()。Take()

我的问题是,对于大型数据集(在测试用例中,我看的是大约有200k行),需要AGES(调试器中约20秒)来执行result.Skip(offset).Take(pageSize) ).ToList();线。 这是使用pageSize = 100和offset = 0。

我的理解是,由于延迟执行,SELECT不会发生,直到它需要(在这种情况下.ToList())。此时,它知道它需要哪些行应该只选择相关的100行。

'data'具有预期的100行,但似乎系统从DB中获取了所有200k个奇数行,然后在代码中完成了分页。

我对LINQ / NHibernate的理解不正确吗? 如果是这样,我想我需要使用条件API执行类似的操作:NHibernate Paging performance (Better option)

1 个答案:

答案 0 :(得分: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方法。