Click here to Skip to main content
14,488,104 members
Rate this:
Please Sign up or sign in to vote.
See more:
Hi, I have a reporting RDLC designed for WinForms. I noticed whenever the RDLC is getting loaded with data, the memory will shoot up by approx. 20MB per request from database. Below are my scenario:
1. Open up a winform with RDLC in it.
2. Click submit button to trigger db call to get data.
3. RDLC is loading ti display data.(this is where the memory started to shoot up by 20MB).
4. Click again the submit button with same parameter, RDLC will reload again(memory shoot up by another ~20MB)

I have cleared off any datasource prior RDLC load. Below are my codes:
protected Microsoft.Reporting.WinForms.ReportViewer rpvBaseReport;

rpvBaseReport.ProcessingMode = ProcessingMode.Local;
rpvBaseReport.LocalReport.SetBasePermissionsForSandboxAppDomain(new PermissionSet(PermissionState.Unrestricted));
rpvBaseReport.LocalReport.DataSources.Clear();

rpvBaseReport.LocalReport.DataSources.Add(reportDataSource);
//further logic here
rpvBaseReport.RefreshReport();


Any idea would be welcomed. Thank you.

What I have tried:

1. Used Performance Profiler to track Memory Usage.
2. Search internet for solution, have tried ReleaseSandBoxAppDomain(), Dispose() but to no avail.
Posted
Updated 18-Apr-19 16:10pm
Comments
Mehdi Gholam 1-Nov-17 3:24am
   
It's to be expected, don't worry about it unless your application crashes or doesn't work correctly.
Jamie888 1-Nov-17 3:36am
   
How to say sir?
Mehdi Gholam 1-Nov-17 3:39am
   
How to say what?
Jamie888 1-Nov-17 3:43am
   
How to say it is expected? Is it the common issue for RDLC?
Rate this:
Please Sign up or sign in to vote.

Solution 1

Hi, I am able to come out with a workaround after many tests. I added below codes into my application and also app.config file:
            rpvBaseReport.LocalReport.ExecuteReportInCurrentAppDomain(AppDomain.CurrentDomain.Evidence);           
rpvBaseReport.LocalReport.AddTrustedCodeModuleInCurrentAppDomain("assembly name here");


As for the app.config:
<runtime>
<NetFx40_LegacySecurityPolicy enabled="true" />
</runtime>


As whenever an event is fired(e.g. click on submit button to fetch data from db and render it into RDLC), a new instance will get created in a new app domain. Thus memory will keep on shoot up and although the resource will get released using proper codes(e.g. Dispose()), it will just get rid of the latest app domain while the previous app domains created will still be there in application cache.
Forcibly ask the RDLC to use only one app domain will prevent a new app domain from being created and thus reduce the memory usage and it will get released every time the form is closed.
Please feel free to correct me or add on any points that I have missed out. Thank you.
   
Rate this:
Please Sign up or sign in to vote.

Solution 2

I'm pretty late to this, but I have a real solution and can explain why!

It turns out that LocalReport here is using .NET Remoting to dynamically create a sub appdomain and run the report in order to avoid a leak internally somewhere. We then notice that, eventually, the report will release all the memory after 10 to 20 minutes. For people with a lot of PDFs being generated, this isn't going to work. However, the key here is that they are using .NET Remoting. One of the key parts to Remoting is something called "Leasing". Leasing means that it will keep that Marshal Object around for a while since Remoting is usually expensive to setup and its probably going to be used more than once. LocalReport RDLC is abusing this.

By default, the leasing time is... 10 minutes! Also, if something makes various calls into it, it adds another 2 minutes to the wait time! Thus, it can randomly be between 10 and 20 minutes depending how the calls line up. Luckily, you can change how long this timeout happens. Unluckily, you can only set this once per app domain... Thus, if you need remoting other than PDF generation, you will probably need to make another service running it so you can change the defaults. To do this, all you need to do is run these 4 lines of code at startup:
LifetimeServices.LeaseTime = TimeSpan.FromSeconds(5);
LifetimeServices.LeaseManagerPollTime = TimeSpan.FromSeconds(5);
LifetimeServices.RenewOnCallTime = TimeSpan.FromSeconds(1);
LifetimeServices.SponsorshipTimeout = TimeSpan.FromSeconds(5);

You'll see the memory use start to rise and then within a few seconds you should see the memory start coming back down. Took me days with a memory profiler to really track this down and realize what was happening.

You can't wrap ReportViewer in a using statement (Dispose crashes), but you should be able to if you use LocalReport directly. After that disposes, you can call GC.Collect() if you want to be doubly sure you are doing everything you can to free up that memory.

Hope this helps!
   

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)




CodeProject, 503-250 Ferrand Drive Toronto Ontario, M3C 3G8 Canada +1 416-849-8900 x 100