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 {…}

    [System.Drawing.ToolboxBitmapAttribute(typeof(CrystalDecisions.Shared.ExportOptions),

    "report.bmp")]

    public class CachedSalesDirectory : Component, IcachedReport {…}

}


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 =

                ConfigurationManager.AppSettings["CrystalServerName"].ToString();

            connectionInfo.DatabaseName =

                ConfigurationManager.AppSettings["CrystalDatabaseName"].ToString();

            connectionInfo.UserID =

                ConfigurationManager.AppSettings["CrystalUserID"].ToString();

            connectionInfo.Password =

                ConfigurationManager.AppSettings["CrystalPassword"].ToString();

            connectionInfo.IntegratedSecurity =

                Convert.ToBoolean(

                ConfigurationManager.AppSettings["CrystalIntegratedSecurity"]);

 

            foreach (CrystalDecisions.CrystalReports.Engine.Table table in tables)

            {

                TableLogOnInfo tableLogOnInfo = table.LogOnInfo;

                tableLogOnInfo.ConnectionInfo = connectionInfo;

                table.ApplyLogOnInfo(tableLogOnInfo);

            }

        }

 

      }

}


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"

                    runat="server"

                    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. We need to cover the difference in how you would setup both approaches so that we can compare performance and also because leveraging the caching management class requires an extra step. 

// Without Caching - To be able to compare performance differences.

private void DisplayDirectoryReport()

{

     SalesDirectory directoryReport = new SalesDirectory();

     directoryReport.Database.Tables.SetLocation(); // Set Connection

 

     // Set the location for any subreport

     foreach (CrystalDecisions.CrystalReports.Engine.ReportDocument rDocument in

              directoryReport.Subreports)

         rDocument.Database.Tables.SetLocation();

 

     CrystalReportViewer.ReportSource = directoryReport;

     CrystalReportViewer.DataBind();

}


// Implementing the caching management class.

private void DisplayDirectoryReport()

{

     Reports.CachedSalesDirectory cachedSalesDirectory = new

             Reports.CachedSalesDirectory();

    

     // Extra step (part of the interface and for the most part will have all the code

     // needed.

     cachedSalesDirectory.CreateReport();

 

     CrystalReportViewer.ReportSource = cachedSalesDirectory;

     CrystalReportViewer.DataBind();

}

 

// Inside CachedSalesDirectory code in red is the code that needs to be added.

public virtual CrystalDecisions.CrystalReports.Engine.ReportDocument CreateReport() {

     SalesDirectory rpt = new SalesDirectory();

     rpt.Site = this.Site;

 

     rpt.Database.Tables.SetLocation();

     foreach (CrystalDecisions.CrystalReports.Engine.ReportDocument rDocument in

        rpt.Subreports)

        rDocument.Database.Tables.SetLocation();

 

     return rpt;

}


The code listing above covers most cases, but what if the report contains parameters. The last listing is going to show how parameters could be streamlined into the Cached[ReportName] class generated for you. Following the example of the SalesDirectory report, we are going to add two properties to the CachedSalesDirectory class.

 

[System.Drawing.ToolboxBitmapAttribute(typeof(CrystalDecisions.Shared.ExportOptions), "report.bmp")]

public class CachedSalesDirectory : Component, ICachedReport {

       

        public string Counties { get; set; }

        public int StateID { get; set; }

 

   …}

 

 

Next step is to set the new properties before calling create.

// Implementing the caching management class.

private void DisplayDirectoryReport(string counties, int StateID)

{

     Reports.CachedSalesDirectory cachedSalesDirectory = new

             Reports.CachedSalesDirectory();

    

     cachedSalesDirectory.Counties = counties;

     cachedSalesDirectory.StateID = StateID;

     cachedSalesDirectory.CreateReport();

 

     CrystalReportViewer.ReportSource = cachedSalesDirectory;

     CrystalReportViewer.DataBind();

}

 

Finally we will change the CreateReport function to account for the parameters.

// Inside CachedSalesDirectory code in red is the code that needs to be added.

public virtual CrystalDecisions.CrystalReports.Engine.ReportDocument CreateReport() {

     SalesDirectory rpt = new SalesDirectory();

     rpt.Site = this.Site;

 

     rpt.SetParameterValue("@Counties", Counties);

     rpt.SetParameterValue("@StateID", StateID);

 

     foreach (CrystalDecisions.CrystalReports.Engine.ReportDocument rDocument in

        rpt.Subreports)

        rDocument.Database.Tables.SetLocation();

 

     return rpt;

}


In conclusion, we have covered a feature of the Crystal Report and .Net framework that allows for faster loading, paging, and grouping of Crystal Report.

Implementing both solutions will prove the efficiency of leveraging the caching functionality.


Similar Articles