• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 5123
  • Last Modified:

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.

  • 6
  • 4
1 Solution
Can you clarify something for me:  Are you hitting the limit as your users page through the reports or are you hitting it due to number of concurrent users?  In other words, if a single user is running the application and paging through a report will they hit this limit eventually or is the problem only because users don't log off so the threads stay active as new users come on?

My thinking at this point is that you could .dispose in your page_unload event, but only do it conditionally - don't dispose if the user is only paging.  This might entail catching the event from the crystal viewer when users page or perhaps hidding the crystal page buttons and placing your own on the form so you can check them for a click event to ignore the dispose.  

DavidAveryAuthor Commented:
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!
DavidAveryAuthor Commented:
This is interesting:


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.

Cloud Class® Course: Microsoft Exchange Server

The MCTS: Microsoft Exchange Server 2010 certification validates your skills in supporting the maintenance and administration of the Exchange servers in an enterprise environment. Learn everything you need to know with this course.

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.
DavidAveryAuthor Commented:
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...
DavidAveryAuthor Commented:
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!
DavidAveryAuthor Commented:
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)
                End If
        End If

    End Sub
DavidAveryAuthor Commented:
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
            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
            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!

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 6
  • 4
Tackle projects and never again get stuck behind a technical roadblock.
Join Now