Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CrystalReports ReportDocument memory leak with database connections

I've spent the past few days looking into that and I can't seem to figure it out.

I have a c# WinForms application that uses the ReportDocument to load a report and put it into the Crystal Report Viewer, so that the user can preview it. The purpose is to preview different statistics and the form is never closed. There's a timer that runs and loads different reports into the viewer.

While that happens the memory usage and handles (I can see them in the task manager) keep increasing. When the application is started, it uses around 30 MB and when it runs for 10 minutes, it is then using around 200 MB and it keeps increasing.

I read a lot about this problem on the internet and I found that both the ReportDocument and the Viewer need to be closed and disposed. Unfortunately, that doesn't fix it. The connection type in the reports is OLE DB(ADO) as the data is retrieved from an SQL Server database.

Briefly what happens is that Form1 has a timer that when elapses it disposes the Crystal Reports Viewer and calls the garbage collector. And then loads the new report.

Here is the sample code that I have

Form1:

private ReportDocument rpt;

private void timer2_Tick(object sender, EventArgs e)
{
    timer2.Enabled = false;

    try
    {
          panel1.Hide();

          if (rpt != null)
          {
               foreach (Table t in rpd.Database.Tables)
                        t.Dispose();
               rpt.Close();
               rpt.Dispose();
               rpt = null;
               GC.Collect();
           }

           panel1.Controls.Remove(CRVviewer);
           if (CRVviewer != null)
           {
               CRVviewer.Dispose();
               GC.Collect();
           }

           // The problem starts from here:

           var report = navigationbar1.CurrentNode;
           rpt = new ReportDocument();
           rpt.Load(@report.Path, OpenReportMethod.OpenReportByDefault);

           rpt.ReportOptions.EnableSaveDataWithReport = false;

           rpt.SetDatabaseLogon(report.UserId, report.Password);

           rpt.VerifyDatabase();

           // It ends here

           CRVviewer = new CrystalReportViewer();
           CRVviewer.ReportSource = rpt;
           CRVviewer.ShowLastPage();
           pagecount = CRVviewer.GetCurrentPageNumber();
           CRVviewer.ShowFirstPage();
           panel1.Controls.Add(CRVviewer);
           this.Update();
    }
    catch(Exception ex)
    {
        ProcessErrors(ex); 
    }
    finally
    {
         timer2.Enabled = true;
    }
}

The problem comes from the DB connections, because if I load a local report, it works fine. But what do I do wrong?

like image 501
Apostrofix Avatar asked Apr 15 '15 06:04

Apostrofix


1 Answers

It's very tricky with Crystal Report to clean up the mess it creates with memory. (No offence to SAP)

You will have to first close and dispose the ReportDocument

rpt.Close();
rpt.Dispose();

And then assign nulls to the ReportViewer and dispose.

CRViewer.ReportSource=null;
CRViewer.Dispose();
CRViewer=null;

And finally, you have to do the two pass GC collect.

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

Please note that it is generally not recommended to call GC.Collect() but sometimes when memory is too much of an issue and third party COM component's like crystal report has issue with getting disposed properly, we may have to go this route.

like image 154
ANewGuyInTown Avatar answered Nov 14 '22 04:11

ANewGuyInTown