Optimizing Crystal Reports for Asp.Net


Implementing and embedding Crystal Reports in the .Net environment is extremely easy to do. Once the tools are downloaded from the businessobject website creating and editing reports from Visual Studio is a breeze. We are going to be covering the caching functionality that the Crystal Report framework provides through the ICachedReport interface.

What you will learn
  • What is the ICachedReport interface.
  • System generated classes and when to modify them.
  • Adding extensions to support ConnectionInfo binding.
  • Putting it all together.

ICachedReport Interface

The ICachedReport interface will work like a flag to signal the Crystal Report framework that the report should be cache. It works by creating a layer ontop of the Asp.net Cache object to accommodate the needs of the report. The interface is found in the CrystalDecisions.ReportSource namespace.

System Generated Classes (Benefits of embedding)

Using Visual Studio to add a report as a new item will generate a report wrapper class with the name of the report. The second class will be the management class named Cached[ReportName]. Visual Studio will generate both classes in the same file (ReportName.cs). Below you will see an example of a generated class for a report called SalesDirectory. For the most part this class will expose everything needed to work with the report without any changes. In some cases when using the Cached class properties will need to be added to support parameters.

namespace Optimized.Reports {
    using System;    using System.ComponentModel;    using CrystalDecisions.Shared;
    using CrystalDecisions.ReportSource;    using CrystalDecisions.CrystalReports.Engine;
    public class SalesDirectory : ReportClass {…}
    public class CachedSalesDirectory : ComponentIcachedReport {…}

Extension Methods for Report

What you will often find is that if the report is not properly authenticated, it will prompt the user everytime the report is loaded. What we will do here is leverage the ConnectionInfo object and create an extension method for the Tables inside the report.

using CrystalDecisions.CrystalReports.Engine;
using CrystalDecisions.Shared;

namespace Core.Util
    public static class Extensions
        /// <summary>
        /// Set Crystal Report ConnectionInfo.
        /// </summary>
        /// <param name="tables">CrystalDecisions.CrystalReports.Engine.Tables</param>
        public static void SetLocation(this Tables tables)
            ConnectionInfo connectionInfo = new ConnectionInfo();
            connectionInfo.ServerName =
            connectionInfo.DatabaseName =
            connectionInfo.UserID =
            connectionInfo.Password =
            connectionInfo.IntegratedSecurity =

            foreach (CrystalDecisions.CrystalReports.Engine.Table table in tables)
                TableLogOnInfo tableLogOnInfo = table.LogOnInfo;
                tableLogOnInfo.ConnectionInfo = connectionInfo;


In the example the values are kept in the WebConfig, but it is not a requirement. If the namespace for the Extension class and the pages that have the controls are not the same-it must be added in order for the method to show.

Putting It Together
Now that we have our SalesDirectory report with the wrapper and utility class, we are going to create a page to hold a report viewer. Below is the code listing for adding the directive to the page and immediately after the declaration for the control.

<%@ Register assembly="CrystalDecisions.Web, Version=13.0.2000.0, Culture=neutral, PublicKeyToken=692fbea5521e1304" namespace="CrystalDecisions.Web" tagprefix="CR" %>

<CR:CrystalReportViewer ID="CrystalReportViewer"
                    AutoDataBind="true" Visible="true" /> 

With the report viewer in place the last thing we need to do is create and bind the report to the viewer.