cancel
Showing results for 
Search instead for 
Did you mean: 
Read only

(Updated) A ReportDocument Loaded From Session Cannot Be Disposed

Former Member
0 Likes
1,278

I have updated post because I have added a sample that I would like Ludek or someone to run and see if they get the same results.  I suspect that I have found a bug in the Crystal Viewer control or at least in how it is intended to be implemented.  I am hoping that I am just missing something simple here and someone can show me what is wrong.  Our problem is that if we cannot get the session and dispose to work in the reports, we cannot release any of the reports into production.  Not being able to page using session and dispose of the reports, effectivly mean that we cannot use Crystal reports.  So I think this is a real issue and I am hoping there is a simple solution or that my code is wrong. 

Here's a summary of the basic problem. 

If you are using ASPX and you take one of the many samples provided on this forum to store the report in session, that works great.  No problems there.  However, if you then try to take another of the many samples provided on this forum to dispose of the report, clear out the windows/temp directory, and free of some of the 75 report objects that the engine can handle, the dispose code conflicts with the session code.  For the past few days I have posted comments asking for a sample that both uses sessiona and disposes of the report object.  No one has been able to provide a sample.  So I suspect that it cannot be done.  I hope that I am wrong because this would effectively invalidate Crystal as a reporting solution for ASPX web applications with users greater than probably 25 to 30 users per day.  So I suspect that I am doing something wrong and I am failing to understand something fundamental here. 

From what I am seeing, if a report object loaded from session, it cannot be diposed of.  I have done LOTS of tests to see if this is correct.  I have tried things like pulling the reprot object back from the viewer control and disposing of it, I have created helper classes that create all of the report objects and dispose of them (which works but only if you never have more than 50 to 75 reports executed per appPool recycle), and I have tried to only create the controls in the viewer.reportsource and never create a local copy, then dispose of the view control and/or pull a reference from the reportsource and dispose of it. Nothing I have tried works.  So I have come to the conclusion that you cannot dispose of a report object that has been loaded from session.  Can anyone show me how to do this?   

The report object disposes fine if you never put it into session.  But as soon as you put it into session and then reload it, you cannot dispose of it.  The Crystal Viewer control will give you an error if you load it from session, dispose of it, then try to page.  If you try to workaround that error and dispose of the viewer control or the reportsource within the viewer control, the dispose works in the code but the windows/temp folder is never cleared.  So it is not actually doing the dispose.  I have tried setting it to nothing, executing the garabage collector, etc.  Nothing I do works. 

I just want to get this to work as it was intended.  I have attached a simple sample that uses a dummy report that contains no logic and only a couple of textboxes.  I put one at the top of the page and one way down at the bottom so that it would create two pages and allow me to test the session. 

I two examples classes and I have commented one out.  The commented one is what is working.  The dispose works if you do not use the session.  So if you regenerate the report every time the user pages, the dispose works exectly as expected.  However, it should also work when loading the report from session. 

The uncommented sample is using the session and dispose.  It will create an error if you page.  If you comment the dispose code, it will work but of course it will not dipose.  If you dispose the injected JavaScript errors on page two because the crystal viewstate hidden field is not created.  I think this is where the error comes into play and where there might be a workaround or solution.  However, if you work around this by commenting the dispose code, you can dispose of the viewer control itself.  Back in v10.2 doing that worked.  It does not work now.  I also tried to create an object, set it to the viewer.reportsource, then dispose of it.  That works in the code but it does not actual dispose of it.  I suspect I am not getting a real reference to it and simply creating a copy. 

Can anyone provide a sample that works for both session and dispose for an ASPX application using Crystal Reports? 

Any help or suggestions is much appreciated

Best regards,

Jon Rothlander

Accepted Solutions (1)

Accepted Solutions (1)

former_member200290
Contributor
0 Likes

Hi Jon,

Thank you for the great description and sample files. This is definitely an issue many users and myself have struggled with. What we need to be aware of is that Page_Load and Page_UnLoad events are called on every page load and every postback event for the page. Even clicking a button with no code behind for it will cause a PostBack and Page_UnLoad event to fire.

This is why you need to place the report into session like you are doing. Remember this is all server side code. The .NET framework offers no provisions for you to know when a user has navigated away from the page as you don't have access to client side events. This means that the report and any other variables you put into session stay active until the session expires.

So what happens when your session expires? Any objects that are stored in session are invalidated. This means that they may still exist in session but are no longer accessible. These invalidated objects stay there until the .NET garbage collector does a clean up. At that point they are deleted and in the case of a Crystal Report its temp files and such will get deleted.

This means that you could have shorter time out for sessions, so objects are invalidated quicker, but you will still be stuck with the garbage collector doing what it needs to do, which happens on its own schedule. Calling gc.collect to do garbage collection does not necessarily help because it would only work in that particular users "domain" inside the greater application pool; and when do you call gc.collect when you don't have access to the client side events?

You also need to be aware that too short of a session time out resulting in prematurely timing out users which may not be too user friendly. Likewise if your session time out is more than an hour long it could also cause issues in session objects being held onto for longer periods. This is a balancing act that you will need to figure out what works best for you and your users.

So to summarize this, you need to put your objects in session to keep them active so you don't have to keep reloading them, but you don't have access to client side events so you have to let clean up happen when ever it happens. This is beyond your or our control as this is just how ASP.NET is designed to work.

Now the experienced developer is going to ask why can't we detect when a user closes the web form, or navigates away from the web form? You can but again this is outside the scope of the .NET framework and it is a path riddled with dangers. So before I go ahead on this I will warn you that "ahead there be dragons".

Client side events can be captured by adding client-side java scripting events to your web forms. This would be Java script code that ties into the events of the browser form and detects when users perform some action. So you could create client side events for OnNavigate and FormClose and such luck that but this will need to be as Javascript outside of .NET. So the first difficulty is that we do not have code examples on how to do this as it is outside of our scope. The second difficult thing is what do you do in that code? You would have to call a new ASPX page that contains clean up code, remove objects from session and gc.collect. But this would require a pop-up or pop-under, which is not always a favourable behaviour for the user, and may not work if the user's browser, Antivirus or general security suite prevents these actions. If the user is navigating away by typing in a new URL like www.google.ca you would have to intercept this navigation, grab the URL, redirect to your page which does the clean up and takes the URL as a parameter. Your page then redirects to the required URL. But again applications can block this type of behaviour and users may not appreciate you are introducing an ability to track their browsing.

I hope this helps you understand the behaviour and how and why things are working the way they are and perhaps gives you direction in how you want to handle this.

Kind Regards,

Trevor Dubinsky

Former Member
0 Likes

Trevor,

That makes a lot of sense, thanks for the details. 

I understand the process and how things work but I have not been able to find a workable solution to using Crystal Reports in an enterprise application.  If we cannot dispose of the report, how can we insure that the resources will be available for other users?  Basically the reporting engine becauses the bottle neck that limits the whole application.  The application I am working on is basically a simple reporting tool for project managers.  So they print a lot of small 1 to 2 page reports with employee billing hours.  They then take those and create invoices (not in our app).  So a common usage is for a project manager to run through 5 to 10 small 1-page reports when creating their invoices.  They might do that in say... 5 to 10 minutes.  So if we had 8 project managers doing that and we cannot dispose of the report objects, that means the last user will get an error.  Is that correct?     

If we have an upwards limited of X-number of report objects actively being held by the engine, what happens if we happen to get more than x-number of users generating a report at the same time?  I know that the odds of having 75 users generate a report at the same time is pretty rare, but since the reports are held in session and have timeouts of 20-minutes (that is what I have them set to and I think I will reduce it to 5 or 10 minutes) we basically cannot have say 10 users walk through say 8 reports each within a timeframe of 20 to 30 minutes without getting an error.  Is that correct?

The remaining issue I have is that in v10.2 this all worked fine.  We have a version of the app running with v10.2 of Crystal.  We are able to create the reportDocument object, set the viewer controls source to the reportDocument, then on the page_unload we were able to dispose of the viewer control, which diposes of the reportdocument and clears up windows/temp. Does that sound wrong?  Maybe we are incorrect and this is not working the way we think it is in v10.2.  If so, that may be leading us down the wrong path.

I am also not clear on how the viewstate hidden field that the Crystal Viewer is creating works as long as you never push the reportDocument into session.  If we do not put the reportDocument into session, everytime we do a page_unload and dispose of the report and once the page is loaded, that viewstate field is there and has value.  However, if we put the reportDocument into session and then pull it back out and set the Viewer's source to the report, the viewstate field is not created.  It looks like if we knew what was controling that viewstate field and could keep it active, we'd be fine.  But we are not sure what the viewer needs to create it.  We can get it to create every time if we do not use sessions, which is what we are doing for now.  We are just recreating the report every time a user does a postback, and it is working and disposing correctly.  But we'd rather use session like we are supposed to.  

Jon

former_member200290
Contributor
0 Likes

Sorry for how long it took to reply. For some reason I did not get the notification that you had replied so I assumed everything was good. I was happened to get this thread from a coworker and see that it was not answered.

So 75 print jobs are the number of jobs that can be opened by our engine at any one time. There is also a limit of three concurrent jobs that can be processed at once but jobs can be queued.

So in your example eight project managers run five single page reports with no subreports, three will get run, five will get queued and worked on once one of the first jobs are idle. This is the important part that if the report is just say multiple pages, it will use one of the concurrent jobs until a page is displayed to the user. Once the user is viewing the first page that concurrent job is released, it is still taking one of your 75 jobs.

Now if you had eight managers try to view 10 reports you would run into a limit. The only solution using just Crystal reports is to limit the amount of reports the user can have in session. For example if a user goes to open a report and they are using a session variable "TheReport" already then pull the report out of session and remove the variable from session then close and dispose the report. Now you can open a new report and use "TheReport" again.

But if you need the potential of ten users each with eight reports open simutaneously and not closing any of the reports then just plain Crystal may not fill that role; you may need to move to something like Crystal Reports Server or BusinessObjects Enterprise.

As for 10.2 it works the same as what we are doing now. If you close and dispose the report and viewer still has the report in it the report will get reopend on every post back. so what you were doing worked, but the performance would be slower because if you are on page 2 and click to go to page 3 the engine will reopen the report and have to generate page 1 and 2 to get you page 3. This is all done through the view state which is what you have observed.

That is why we suggest to leave the report in session so that when the user goes to the next page we only need to generate the next page. So perhaps in this situation it would be to only allow one report session variable per user.

Trevor

Former Member
0 Likes

Dear JON ,

Me Too struggling with above queries from last 8 months , but i didn't get a solution to dispose report object while using session ,

At last i started to generate my report in PDF Format .

rpt.ExportToHttpResponse(CrystalDecisions.Shared.ExportFormatType.PortableDocFormat, Response, False, "TrailBalance")

Please check my post here :

http://scn.sap.com/thread/3215478

Still my Question is :

1. Is there any other way to generate report without using session ?

         If answer is NO , then why NOT SAP can arrange alternate solution for this .

         if YES what is the solution .  

former_member373051
Participant
0 Likes

Dear Dubinsky,

                     i searched in lot of SAP community forums.  i think this question has not answered from last 4years. how to resolve this issues, what is the solution for this.?

Former Member
0 Likes

Re-read the post from Trevor - Jun 15, 2012 12:44 PM

- Ludek

Answers (0)