使用QueryOver<>获取大型数据时出现OutOfMemory异常在Linq到Nhibernate

时间:2012-06-06 12:33:52

标签: .net linq-to-nhibernate nhibernate-3

我有一个在NHibernate 3.0中使用Linq到NHibernate的asp.net Web应用程序。

在一个函数中,我需要从一个包含1000列记录和20列的表中获取大约20000条记录。

我正在使用Session.QueryOver<>()方法来获取记录。

功能代码是:

public IList<BatchDetails> GetBatchRecordsOnBatchId_BatchSize(int batchId,int stratingRowdId,int batchSize)
    {
        // If the stratingRowdId will be 0 than frist count of the Batch Records equivlent to the batchSize will be return

        //If the batchSize will be 0 than all the Batch Records starting from the RowId equivlent to the stratingRowdId will be return

        // If both the stratingRowdId & batchSize are 0 than all the BatchReocrds for the BatchId will be return



        if (batchId <= 0)
        {

            throw new ArgumentException();
        }
        using (var session = _sessionFactory.OpenSession())
        {
            using (var transaction = session.BeginTransaction())
            {
                try
                {
                    //Get Batch data from the Database for the BatchId
                    var batchData = from batchRecords in session.QueryOver<BatchDetails>()
                                        .Where(x => x.BatchId == batchId)
                                        .List().Skip(stratingRowdId).Take(batchSize)
                                    select batchRecords
                        ;

                    transaction.Commit();
                    return batchData.ToList();
                }
                catch (ArgumentException ex)
                {
                    if (transaction != null) transaction.Rollback();


                    throw;
                }
                catch (Exception exception)
                {
                    if (transaction != null) transaction.Rollback();

                    throw;
                }
                finally
                {
                    session.Flush();
                }
            }

        }
    }

此代码正在运行,直到该表有2个lac记录。

但是在表中添加了10个lac记录后,此方法会抛出错误:

  

NHibernate.Util.ADOExceptionReporter |抛出了'System.OutOfMemoryException'类型的异常。

     

NHibernate.Util.ADOExceptionReporter |抛出了'System.OutOfMemoryException'类型的异常。

     

NHibernate.Util.ADOExceptionReporter |抛出了'System.OutOfMemoryException'类型的异常。

     

[DAL.GetBatchRecordsOnBatchId]:应用程序中出现未保留错误,异常:无法执行查询   [将this_.ReferenceId选为Referenc1_2_0_,this_.AccountNumber为AccountN2_2_0_,this_.AccountStatus为AccountS3_2_0_,this_.AccountType为AccountT4_2_0_,this_.AccountSubType为AccountS5_2_0_,this_.AccountDescription为AccountD6_2_0_,this_.ActivationDate为Activati7_2_0_,this_.CombinedBilling为Combined8_2_0_, this_.PlanAmount为PlanAmount2_0_,this_.PlanCode为PlanCode2_0_,this_.CustomerName为Custome11_2_0_,this_.CustomerEmail为Custome12_2_0_,this_.CustomerPhone为Custome13_2_0_,this_.CustomerAddress为Custome14_2_0_,this_.CustomerCity为Custome15_2_0_,this_.CustomerState为Custome16_2_0_,this_。 CustomerZipCode为Custome17_2_0_,this_.PaymentAmount为Payment18_2_0_,this_.PaymentCurrency为Payment19_2_0_,this_.PaymentDate为Payment20_2_0_,this_.TransactionId为Transac21_2_0_,this_.BatchId为BatchId2_0_ FROM TIOTestDB.dbo.BatchDetails this_ WHERE this_.BatchId =? ]   位置参数:#0&gt; 3   [SQL:选择this_.RefereId作为Referenc1_2_0_,this_.AccNumber作为AccountN2_2_0_,this_.AcStatus作为AccountS3_2_0_,this_.AcType作为AccountT4_2_0_,this_.AccSubType作为AccountS5_2_0_,this_.AccountDescription作为AccountD6_2_0_,this_.ActivationDate作为Activati7_2_0_,this_.CombinedBilling as Combined8_2_0_,this_.PlanAmount为PlanAmount2_0_,this_.PlanCode为PlanCode2_0_,this_.CustomerName为Custome11_2_0_,this_.Email为Custome12_2_0_,this_.Phone为Custome13_2_0_,this_.Address为Custome14_2_0_,this_.City为Custome15_2_0_,this_.State为Custome16_2_0_, this_.ZipCode为Custome17_2_0_,this_.PayAmount为Payment18_2_0_,this_.Currency为Payment19_2_0_,this_.PayDate为Payment20_2_0_,this_.TransactionId为Transac21_2_0_,this_.bhId为bhId2_0_ FROM bDetails this_ WHERE this_.bhId =?]

因为我在foreach()迭代中执行此函数, 虽然查询执行1或2次并检索数据但在此之后它会抛出内存不足异常。

作为一名经验丰富的NHibernate开发人员,我可以理解我需要重构LINQ查询以优化其性能。

我也在互联网上搜索过但我无法获得太多信息。

最早的回复将不胜感激。

1 个答案:

答案 0 :(得分:3)

首先,您的查询:

from batchRecords in session.QueryOver<BatchDetails>()
    .Where(x => x.BatchId == batchId)
    .List().Skip(stratingRowdId).Take(batchSize)
select batchRecords

可以简化为:

session.QueryOver<BatchDetails>()
    .Where(x => x.BatchId == batchId)
    .List().Skip(stratingRowdId).Take(batchSize)

它的行为完全相同。您的fromselect所做的就是在查询末尾添加.Select(x => x)

现在,您的问题是您过早地致电List()List()的作用是将查询的所有结果检索到内存中。如果之后使用LINQ方法(就像使用Skip()Take()那样),它们将在内存中执行,这不是你想要的。

您应该做的是将List()移到查询的末尾:

session.QueryOver<BatchDetails>()
    .Where(x => x.BatchId == batchId)
    .Skip(stratingRowdId).Take(batchSize)
    .List()

这样,Skip()Take()都将被转换为SQL,这意味着您不会将所有数据都检索到内存中,只检索您实际需要的数据。这应该可以修复你的OOM。