2 out of 4 rated this helpful - Rate this topic

Using the ReportViewer ASP.NET Control in Windows Azure

This article was written using: Windows Azure SDK 1.6, Visual Studio 2010 ReportViewer control for ASP.NET (Microsoft.ReportViewer.WebForms.dll version 10.0.40219.329)

The MSDN articles Getting Started Guide for Application Developers (Windows Azure SQL Reporting) and How to: Use ReportViewer in a Web Site Hosted in Windows Azure contain information on using the ReportViewer control for ASP.NET in a Windows Azure application with SQL Reporting. This article provides key additional information for successful use of ReportViewer in a Windows Azure website that uses more than one web role instance.

This article will outline two solutions that will allow you to deploy ReportViewer solutions in a multi-instance Windows Azure website.

IReportServerCredentials causes ASP.NET Session State to be used

As noted in the documentation for IReportServerCredentials, when the ReportViewer uses an implementation of this interface, it will use session state to store the object and the ReportViewer state.

When using a single web role instance, the ASP.NET ReportViewer control will work correctly without explicitly setting sessionState in the web.config. It will work just by implementing IReportServerCredentials and setting ReportViewer.ServerReport.ReportServerCredentials to an instance of your IReportServerCredentials implementation. This will work because it will automatically use the default in-process session state provider. With only a single web role instance, requests will always hit the same instance, and the session state will always exist on the instance. Of course the instance will be rebooted for Windows patches, and it could be rebooted to move to another physical host machine in the Azure data center. Reboots of the instance will cause the in-process session state to be lost.

Multiple web role instances hosting the ReportViewer

When you use multiple instances of the web role (for high availability and scale-out) for your solution, and you are using the default in-process session state provider, you will intermittently receive the following error when loading the web page that contains a ReportViewer control:

ASP.NET session has expired or could not be found 

This error occurs because your initial request hit a single web role instance and had its session state stored in-process on that web role instance. A subsequent request from the same browser might hit one of the other web role instances, where the session state does not exist.

The most direct approach is to use the Azure Caching session state provider to provide session state across all instances as described in Session State Provider for Winddows Azure Caching. Unfortunately, the use of Azure Caching session state provider surfaces a bug in the NetDataContractSerializer when using the ReportViewer control that comes with Visual Studio 2008 or Visual Studio 2010. The ASP.NET Session State Provider for Windows Azure Caching uses the NetDataContractSerializer to serialize objects into session state.

The bug causes an exception to be raised by the web page that contains a ReportViewer control (along with a stack trace):

Server Error in '/' Application.

Type 'Microsoft.Reporting.WebForms.SyncList`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]' is an invalid collection type since it does not have a default constructor.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.Runtime.Serialization.InvalidDataContractException: Type 'Microsoft.Reporting.WebForms.SyncList`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]' is an invalid collection type since it does not have a default constructor.

noteNote
The ReportViewer control that will ship with the next version of Visual Studio after Visual Studio 2010 should have this issue fixed.

Solutions

There are two possible solutions for successful use of ReportViewer:

  1. Make the ReportViewer "session-less" to prevent it from using ASP.NET session state. This is the preferred method.

  2. Install hotfix 2624480 (http://support.microsoft.com/kb/2624480) as a startup task in the role instance. This hotfix fixes an issue with the NetDataContractSerializer. This approach will cause all role instance reboots to be slower because the startup task to install the hotfix must complete before the instance is available.

"Session-less" ReportViewer

You can find a discussion of making ReportViewer “session-less” in the following articles:

Instead of implementing IReportServerCredentials and assigning an instance to ReportViewer.ServerReport.ReportServerCredentials, you can implement IReportServerConnection or IReportServerConnection2 and point to this class in your web.config file.

The following code is an implementation of IReportServerConnection (with error handling removed for brevity). This implementation simply retrieves the Report Server user name, password, and server name from the web.config AppSettings:

noteNote
Note that this code is based on the Windows Azure ReportViewer Control Sample linked to from the Windows Azure SQL Reporting Samples TechNet Wiki page.

namespace ReportViewerRemoteMode
{
    public sealed class ReportServerConnection : IReportServerConnection
    {
        public bool GetFormsCredentials(out Cookie authCookie, out string user, 
                                        out string password, out string authority)
        {
            authCookie = null;
            user = ConfigurationManager.AppSettings["RSUSERNAME"];
            password = ConfigurationManager.AppSettings["RSPASSWORD"];
            authority = ConfigurationManager.AppSettings["RSSERVER_NAME"];
            return true;
        }
 
        public WindowsIdentity ImpersonationUser
        {
            get { return null; }
        }
 
        public ICredentials NetworkCredentials
        {
            get { return null; }
        }
 
        public Uri ReportServerUrl
        {
            get 
            {
                return new Uri(String.Format("https://{0}/reportserver", 
                               ConfigurationManager.AppSettings["RSSERVER_NAME"]));
            }
        }
 
        public int Timeout
        {
            get { return 60000; }
        }
    }
}

Assuming that your assembly name is ReportViewerRemoteMode, the following entry in the <appSettings> section in the web.config will cause the ReportViewer control to use this IReportServerConnection implementation:

<appSettings>
    <!-- Custom Report Server Connection implementation-->
    <add key="ReportViewerServerConnection" 
         value="ReportViewerRemoteMode.ReportServerConnection, ReportViewerRemoteMode"/>
  </appSettings>

Do not set the ReportViewer.ServerReport.ReportServerCredentials or the ReportViewer.ServerReport.ReportServerUrl in your web page code-behind class.

This solution will not work for all scenarios because the web.config can only point to one IReportServerConnection implementation.

Hotfix 2624480 installed as startup task

Another solution is to install hotfix 2624480, http://support.microsoft.com/kb/2624480, as a startup task in the role instance. This hotfix fixes an issue with the NetDataContractSerializer that will allow the ReportViewer control to successfully serialize to session state without throwing an exception.

In order to implement this solution, you will need the "x64" version of the hotfix executable downloaded from http://support.microsoft.com/kb/2624480. Add the file "NDP40-KB2624480-x64.exe" to your Visual Studio project, and set the file properties to copy to the output directory. Then in the ServiceDefinition.csdef file, you need to add a <Startup>element inside the <WebRole> element as follows. This causes the hotfix to silently install without user prompting or intervention.

noteNote
Note the "/q" parameter for the .EXE

<Startup>
  <Task commandLine="NDP40-KB2624480-x64.exe /q" 
        executionContext="elevated" />
</Startup>

Now when the role instance starts, it will install the hotfix before the instance starts accepting web requests.

One drawback of this approach is that it will cause all role instance reboots and deployments to be slower because the startup task to install the hotfix must complete before the instance is available. This can cause deploy times and startup times to be significantly slower. A test of this approach with deploying two "small" web role instances to Windows Azure from within Visual Studio showed that deployments took twice as long when using this startup task to install the hotfix.

If possible, make the ReportViewer "session-less". If this is not possible, then install the hotfix as a startup task as a fallback method.

Additional Resources


Build Date:

2013-04-18
Did you find this helpful?
(1500 characters remaining)

Community Additions

ADD
© 2013 Microsoft. All rights reserved.