Link to home
Start Free TrialLog in
Avatar of DavidAvery
DavidAvery

asked on

asp.net usage of web viewer and postback problems

Hello all

I am getting the error: The maximum report processing jobs limit configured by your system administrator has been reached, using CRW XI R2.

Problem Description  

This error is occuring on a custom web reporting application previously running well in crystal 10 (Enterprise).  I have researched the problem to be my lack of calling a reportdoc.dispose on page_unload.  Here's my problem.  I am using asp.net 1.1, and I am using the crystal reportviewer.  Most reports are multi-page, so the users run the report and browse around it as needed.  To keep the report from re-running as they page through, I am caching the reportdocument to a session variable, and restoring it if the session variable is intact.  My problem is that if I do not call reportdocument.dispose in page_unload, I get the The maximum report processing jobs limit configured by your system administrator has been reached error.  If I call the dispose, when pagination occurs, I cannot rely upon my cached reportdocument, and I get another error (invalid object).  I think it’s pretty inefficient to re-run the report between every page, some take 15-20 seconds to process.  I also launch one of 12 different reports to separate popup windows for viewing, so it is important that each popup window retain it’s own state. Can you assist me with a strategy for doing this effectively?  BO samples that are distributed with the report tool do not dispose their reportdocuments at all.  

I have thought about pulling the dataset first, and caching that and pushing it at the report, but I would prefer not to rewrite the reports.  One BO tech came up with a solution where JS onclose launched a cleanup which was pretty ugly.  I am hoping that there are perhaps techniques for caching the report object that can make this work.  

Note that expanding the number of reports using the registry key is not a valid solution, without a dispose, the sessions are closing leaving the reportdocuments open.

Thanks!
ASKER CERTIFIED SOLUTION
Avatar of frodoman
frodoman
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of DavidAvery
DavidAvery

ASKER

Thanks Frodoman.  

I am hitting the max 75 connections only because I am not disposing at all to keep the viewer live.  I guess the problem is that I believe that simply destroying the session object is not releasing the usage of the reportdocument.  Therefore as the sessions die, there is a handle to a reportdocument still live that is not being collected.  

My solution launches reports in popup windows which retain autonomy to page/rerun, etc.  I guess with your suggestion I would build in a JS onclose that could launch another page whose sole purpose was to unpack session, release the reportdocument, and kill the sessvar.

Do you know of a way to see usage?  This would certainly help test your theory.  There used to be a way to do it with CRW10.  Thank you!
This is interesting:

http://msdn2.microsoft.com/en-us/library/ms225490(VS.80).aspx

Crystal Reports for Visual Studio 2005  
Use the Close() Method to free up the report  
Another way to optimize scalability in a Crystal Reports for Visual Studio 2005 project is to use one of the available Close() methods to release the memory that is used by the report.

Two Close() methods are available:

ReportDocument.Close() that is used with Crystal Reports.
ReportClientDocument.Close() that is used with unmanaged RAS or managed RAS.
The ReportDocument.Close() method

When using Crystal Reports for Visual Studio 2005, you can use the ReportDocument.Close() method to release the memory that the Crystal report consumes on the Web server.

How the ReportDocument.Close() method is accessed depends on whether the report is embedded or non-embedded:

If the report is embedded, a report wrapper class is generated to represent the report in code. This report wrapper class inherits from ReportDocument, and the Close() method is accessed by inheritance.
If the report is not embedded, it is loaded from the file directory into an instance of ReportDocument, and the Close() method is accessed directly from the ReportDocument class.
Note   For more information on embedded and non-embedded reports, see Should I Use Embedded or Non-embedded Reports?.
Both the Crystal report and the instance of the ReportDocument each consume memory. When the ReportDocument is freed from memory, the report continues to use memory.

For example, the ReportDocument instance falls out of scope when the Web page finishes loading. When the garbage collection for .NET disposes of the ReportDocument instance, the memory that is used by the ReportDocument instance is released from the Web server.

However, the report itself remains in memory on the Web server. It cannot be removed, because a ReportDocument instance no longer exists to access the report. When those circumstances are repeated in a highly scaled situation, the memory on the Web server fills with reports that are no longer being accessed.

To resolve that problem, call the ReportDocument.Close() method. The report itself is closed on the Web server and the memory is released for further reports.

When to call the ReportDocument.Close() method

The ReportDocument.Close() method should not be called on the page before the report has been displayed because, even if the report has been closed, ReportDocument will reopen the report if it is referenced again. The Close() method should only be called after the display process is complete.

The right time to call the Close() method is during the Page_Unload event.

The ReportClientDocument.Close() method

When using an unmanaged RAS or managed RAS server, reports are stored on the Report Application Server, but they are represented on the Web server by an instance of ReportClientDocument. If the ReportClientDocument instance passes out of scope without calling the ReportClientDocument.Close() method, the Report Application Server keeps the report open in memory, even though the report can no longer be accessed. When those circumstances are repeated in a highly scaled situation, the memory on the Report Application Server fills with reports that are no longer being accessed on the Web server.

To resolve that problem, call the ReportClientDocument.Close() method. The report is closed on the Report Application Server and the memory is released for further reports.

When to call the ReportClientDocument.Close() method

The Close() method should not be called on the page before the report has been displayed, because the report must remain open on the server until the display process has been completed.

For a ReportClientDocument instance, the Close() method immediately closes the report and the report cannot be reopened. Therefore, if the Close() method is called before the report is displayed, the report will be inaccessible and an exception will be thrown.

The right time to call the Close() method is during the Page_Unload event.

No, I don't know of a way to see usage - I suspect there is one but I've never gone looking for it.  I am certain that you are correct that destroying the session object does not dispose the reportdocument object - they are completely different objects.  You'll probably have better luck destroying the reportdocumentobject because that I believe is the one hanging on to your threads - the session is just data.

Honestly, what I typically do in my web applications is avoid all of the paging and caching issues completely.  My typical approach is to create the report and use the ExportToStream function to stream the results to the browser in PDF format.  My user base is comfortable navigating within a PDF, the Acrobat Reader has better search functionality than the Crystal viewer, and there are no threading or paging problems.  I do this routinely on all but the simplest reports.
Yeah, I'm getting tired of fighting this battle.

Truly, I would like to be able to offer the user dashboard style reports for home pages with drill down and other functions, which is in all of the flash crystal adverts.  I also have a paid incident into BO right now.  It just bothers me that they tout interactive web reporting, and IMO, it just doesn't work.
I've heard the same comments many times in this forum and I share those feelings...
I'm going to try session_end.  I was thinking of using a ht of session keys, and looping through each, directcasting it to reportdocument and disposing.  Wish me luck!
Looking good... testing found it running for all sessions, cast back to reportdocument worked :)  Doing a load test now.

    Sub Session_End(ByVal sender As Object, ByVal e As EventArgs)
        ' Fires when the session ends
        'kill off any reporting objects
        Dim ht As Hashtable
        Dim rdc As ReportDocument
        ht = Session("sessRpt")
        If Not ht Is Nothing Then
            For Each sKey As String In ht.Keys
                If Not Session(sKey) Is Nothing Then
                    rdc = CType(Session(sKey), ReportDocument)
                    rdc.Close()
                    rdc.dispose()
                End If
            Next
        End If

    End Sub
Cool!!! it works!

I drop the report into session as standard with crystal, but also maintain a HT of sesskeys to unpack later.  I'm certain this can be improved on, but it works for me.  It's a shame that a paid call to CRW couldn't net a solution.

      Private Sub ConfigureCrystalReports()
        Dim sk As String = Request.QueryString("SK")
        If (Session(sk) Is Nothing) Then
            rdoc = New ReportDocument
            setupReport()
            Session(sk) = rdoc
            Dim HT As New Hashtable
            If Not Session("sessRpt") Is Nothing Then
                HT = CType(Session("sessRpt"), Hashtable)
            End If
            HT.Add(sk, System.DBNull.Value)
            Session("sessRpt") = HT
        Else
            rdoc = CType(Session(sk), ReportDocument)
        End If

        crv.ReportSource = rdoc
    End Sub


I have no dispose in my form_unload.  I set the number of printjobs down to 1 and ran reports using a bot until it gave me the error.  I waited a moment, and watched in debug mode as it unpacked and disposed all of the hanging reportdocuments.

Thanks frodoman for giving me some food for thought.
Sure thing - glad you've got it working!

frodoman