views:

225

answers:

2

I have the following situation:

Microsoft Report Viewer 2010 is used to display reports (.rdlc files) in local mode in an ASP.NET web application. The report data is supplied by assigning a datasource in the code behind of an ASPX page. Here's an example:

if(!IsPostBack){
ReportViewer1.Reset();
ReportDataSource reportDataSource = new ReportDataSource();

reportDataSource.Name = "DataContainerType";
reportDataSource.Value = DatasourceOnPage;
reportDataSource.DataSourceId = "DatasourceOnPageID";
reportDataSource.DataMember = "DataSourceView";

ReportViewer1.ProcessingMode = ProcessingMode.Local;
ReportViewer1.LocalReport.DisplayName = "ReportName";
ReportViewer1.LocalReport.ReportEmbeddedResource = "Reportfile.rdlc";
ReportViewer1.LocalReport.DataSources.Add(reportDataSource);
}

Normally this works great, every report we have loads just fine.

When I leave the page open until the worker process recycles, then try to refresh the report or do any kind of postback on the report page, I get an (unhandled) exception "ASP.NET Session has expired".

Exception information: Exception type: AspNetSessionExpiredException Exception message: Die ASP.NET-Sitzung ist abgelaufen oder konnte nicht gefunden werden. at Microsoft.Reporting.WebForms.ViewerDataOperation..ctor() at Microsoft.Reporting.WebForms.HttpHandler.GetHandler(String operationType) at Microsoft.Reporting.WebForms.HttpHandler.ProcessRequest(HttpContext context) at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

IIS is set to recycle every morning at 5 a.m. We use the InProc setting in the web.config, so obviously, the session is lost. If I understand the ASp.NET 2.0 behaviour correctly, the unhandled exception should cause the worker process to terminate. There should be a new worker process when the next request arrives. Somehow this seems to affect our Sitemap Provider, because on the production system, almost every time (but not always), the TreeView and SiteMapPath controls stop displaying the sitemap, but throw no errors. This is very bad, because the users can not recover from this state without the admins recycling the application pool.

If the session timeout setting is low, it causes the same exception. This seems odd, because from what I read, the Report Viewer should ping the server to keep the session alive.

I tried to catch the exception, but it is thrown somewhere inside the ReportViewer control, before I get to access it in page load. The code above is still executed if I take out the IsPostBack, but it has no effect.

I tried using state server instead of keeping the session in the process, so it would not lose it's session, but this leads to other errors from the report viewer. It seemed to be unable to store it's session data in the state server.

The environment is Windows Server 2003 .NET 4.0 Report Viewer 2010

Where can I either handle the error, without having the worker process terminate, or get the report to use stateserver?

Help me Stack Overflow, you're my only hope!

A: 

Have you tried limiting the number of worker processes to 1?

I was having the same issue, and what fixed it for me was A) setting a specfic time of day for recycle (which you already have) and B) limit worker processes to max of 1 in app pool settings.

VicarInATutu
We never changed the number of worker processes, it's still 1.I now implemented a check if the session is new, then reload the page. I'm waiting for the results from the test system to see if it helps.
Niels Schultz
+1  A: 

I now solved it, by using a custom page derived from the base page class. I now check if the session is new, then redirect to the same page, thereby reloading the report from sratch.

I also used a script to keep the session alive, since the report viewer is not doing this by itself. But since I'm satisfied with the session check, I disabled it.

public class ReportPage: System.Web.UI.Page
{

    protected override void OnInit(EventArgs e)
    {
        base.OnInit(e);

        #region check for lost session
        if (Context.Session != null)
        {
            if (Session.IsNewSession)
            {
                string cookieHeader = Request.Headers["Cookie"];
                if ((null != cookieHeader) && (cookieHeader.IndexOf("ASP.NET_SessionId") >= 0))
                {
                    Response.Redirect(Request.Url.ToString());
                }
            }
        }

        #endregion check for lost session

        #region generate keepsessionalive script 
        /*

        StringBuilder sb = new StringBuilder();
        sb.Append("$(function () {setInterval(KeepSessionAlive, " + GetSessionTimeoutInMs() + ");");
        sb.Append("});");
        sb.Append("function KeepSessionAlive() {");
        sb.Append(string.Format("$.post('{0}', null);", ResolveUrl("~/KeepSessionAlive.ashx")));
        sb.Append("};");

        // register on page
        // Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "SessionKeepAlive", sb.ToString(), true);
        */
        #endregion generate keepsessionalive script
    }

    private int GetSessionTimeoutInMs()
    {
        return (this.Session.Timeout * 60000) - 10000;
        //return 100000;
    }
}

The keep-alive-script calls an http handler (.ashx file) that touches the session every time it's called (not sure actually if this is necessary). Here it is for the record:

 public class KeepSessionAlive : IHttpHandler, IRequiresSessionState
{

    public void ProcessRequest(HttpContext context)
    {
        context.Session["KeepSessionAlive"] = "KeepAlive!";
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}
Niels Schultz

related questions