Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

An Open Source RDL Engine

4.77/5 (12 votes)
20 Dec 2010CPOL3 min read 85.2K   3.5K  
An Open Source RDL engine for rendering reports to WinForms or ASP.NET

Introduction

The Sawiki RDL Reporting engine is used to render RDL reports to an ASP.NET or Winforms viewer. It can also be export rendered reports to XLS, PFD, or CSV format files.

The RDL Engine reads RDL files created in Microsoft Visual Studio, connects to the referenced database, collects parameter information and renders the resulting report. It currently supports all RDL features with the exception of 3D charts, Columns, Recursive hierarchies, Document Maps, and Fixed Headers. It includes full VB.NET support including importing external libraries.

This is a lightweight high speed report renderer and includes an RDL compiler to further improve performance of report projects. It is useful to anyone wishing to deploy Microsoft Reporting Services report to a web site without having to pay for a SQL Server license, or to deploy reports in a WinForms applications.

Background

This project was started as a means of including RDL reports into a web site without having to pay Microsoft for a SQL Server licence for the web server. Also I didn't like the way the Microsoft ASP.NET report viewer worked. I spent some time with the open source project RDL Project 3 but was unhappy with the partial VB interpreter and the complex rendering algorithms.

The Engine uses the VBCodeProvider class from Microsoft.VisualBasics namespace to compile any included source and expressions into an assembly. Each function in the assembly is put into a list of delegates which can be called by index during report processing. The only expressions that can't be compiled directly are ones including the aggregate functions like Sum, Avg, etc. For these, the VB source code from the RDL report is modified using regular expressions to wrap any aggregate function call into an iterator.

Rendering is done to an intermediate model and then rendered to the final form. The intermediate model is represented by the GenericRender class which is returned by the Report.Run method. The GenericRender represents a simple box model which can be easily translated to one of the final rendering classes. These include RenderToHtml, RenderPagesToHtml, RenderToText, RenderToCsv, RenderToXls, and RenderPagesToPdf. The WinForms viewer works directly with the GenericRender class. The simple box model of the GenericRender class allows simple rendering to the various final forms which is evidenced by the RenderToText class being less than 100 lines of total code.

Using the Code

The RDL Engine includes four elements:

  • RDLEngine - The base library which includes the RDL interpreter and rendering engine. This needs to be included in any reporting project.
  • RDLAsp - The ASP.NET library including a report viewer control, a parameters control, a print function, and an export function.
  • RDLViewer - The WinForms library including a report viewer, a parameters control, a parameters dialog, print and export functions
  • RDLCompile - The RDL compiler for compiling an RDL report into a .NET assembly.

To read in an RDL report, set some parameters and write the rendered report out to PDF and XLS use the following:

C#
// Create a new Report object and load an RDL report definition
Rdl.Engine.Report rpt = new Rdl.Engine.Report();
FileStream fs = new FileStream("Product Line Sales.rdl", FileMode.Open);
rpt.Load(fs, ".");
fs.Close();

// Set parameters
rpt.ReportParameters["Category"].Value = 1;
rpt.ReportParameters["StartDate"].Value = DateTime.Now;
rpt.ReportParameters["EndDate"].Value = DateTime.Now.AddMonths(-1);

// Render the report
Rdl.Render.GenericRender render = rpt.Run();

// Export to XLS and write to a file
Rdl.Render.RenderToXls xlsExport = new Rdl.Render.RenderToXls();
string xls = xlsExport.Render(render, false);
StreamWriter sw = File.CreateText("Product Line Sales.xls");
sw.Write(xls);
sw.Close();

// Export to PDF and write to a file
PageRender pr = new Rdl.Render.PageRender();
pr.Render(render);
Rdl.Render.RenderPagesToPdf pdfExport = new Rdl.Render.RenderPagesToPdf();
string pdf = pdfExport.Render(render, pr);
sw = File.CreateText("Product Line Sales.pdf");
sw.Write(pdf);
sw.Close();

The following ASP.NET page includes a ReportViewer control and a Parameters control. When Button6 is pressed, the Parameters control is loaded with the Product Line Sales report. The Parameters control includes a View button which when pressed triggers the ViewReport event in which the report is rendered and loaded into the ReportViewer control.

ASP.NET
<%@ Page Language="C#" MasterPageFile="~/Sawiki.master" 
AutoEventWireup="true" CodeFile="Samples.aspx.cs" 
Inherits="_Samples" Title="Sawiki Software Samples" %>
<%@ Register TagPrefix="uc" Assembly="RdlAsp" 
Namespace="RdlAsp" %>
<%@ Register Assembly="AjaxControlToolkit" 
Namespace="AjaxControlToolkit" TagPrefix="cc1" %>
<%@ Register Assembly="System.Web.Extensions, 
Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
    Namespace="System.Web.UI" TagPrefix="asp" %>
<asp:Content ID="Content1" ContentPlaceHolderID="
ContentPlaceHolder1" Runat="Server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<div>
    <asp:Button ID="Button6" runat="server" 
    OnClick="ProductLineSales_Click" Text="Product Line Sales" />
    <table>
        <tr>
            <td>
            <uc:Parameters ID="ReportParameters" 
            runat="server" OnViewReport="
            ReportParameters_ViewReport" />
            </td>
        </tr>
        <tr>
            <td>
            <uc:ReportViewer ID="ReportViewer" runat="server" />
            </td>
        </tr>
    </table>
     
</div>
</asp:Content>

With the code behind:

C#
protected void ReportParameters_ViewReport
	(object sender, RdlAsp.ViewReportEventArgs e)
{
    // When the ParameterEntry control posts this event 
    // render the passed report and load the ReportViewer with it
    Rdl.Engine.Report rpt = e.Report;
    Rdl.Render.GenericRender render = rpt.Run();
    ReportViewer.SetReport(render);
}

protected void ProductLineSales_Click(object sender, EventArgs e)
{
    // Load the report
    Rdl.Engine.Report rpt = new Rdl.Engine.Report();
    string path = Request.MapPath("SampleReports\\Product Line Sales.rdl");
    FileStream fs = new FileStream(path, FileMode.Open);
    rpt.Load(fs, System.IO.Path.GetDirectoryName(path));
    
    // Put the report into the Parameters control
    ReportParameters.SetReport(rpt);
    ReportViewer.SetReport(null);
}

In the following WinForms example, it is assumed that you have a form with a Parameters control, a ReportViewer control, and a button named btnEmployeeSalesSummary. When the button is pressed, the Employee Sales Summary report is loaded and put into the Parameters control. When the Parameters view button is pressed, the ViewReport event is raised where the report is rendered and put into the ReportViewer control. In this example, the InitializeDataSet event is registered which may be used if the programmer wants to substitute a custom data table for the one defined in the RDL report.

C#
private void Form1_Load(object sender, EventArgs e)
{
    parametersEntry1.ViewReport += 
	new EventHandler<RdlViewer.ParametersEntry.ViewReportEventArgs>(ViewReport);
}

static void rpt_InitializeDataSet(object sender, 
	Rdl.Runtime.InitializeDataSetEventArgs args)
{
    switch(args.DataSetName)
    {
        case "SalesEmps":
            // You could replace the default data with a 
            // custom data table for any of the named datasets here.
            //e.dt = {your custom datatable}
            break;
        default:
            break;
    }
}

private void ViewReport(object sender, RdlViewer.ParametersEntry.ViewReportEventArgs args)
{
    // When the ParameterEntry control posts this event 
    // render the passed report and load the ReportViewer with it
    Rdl.Render.GenericRender render = args.Report.Run();
    reportViewer1.SetReport(render);
}

private void btnEmployeeSalesSummary_Click(object sender, EventArgs e)
{
    // Load the report and put it into the ParameterEntry control
    Rdl.Engine.Report rpt = new Rdl.Engine.Report();
    rpt.Load(new FileStream(@"Employee Sales Summary.rdl", 
		FileMode.Open, FileAccess.Read, FileShare.Read),
        @".");
    rpt.InitializeDataSet += rpt_InitializeDataSet;
    
    parametersEntry1.SetReport(rpt);
}

References

The home of the SawikiSoft RDL Engine project. You can find viewable sample reports and the current source code at this site.

History

  • 17th December, 2010: Initial version

License

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