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:
Rdl.Engine.Report rpt = new Rdl.Engine.Report();
FileStream fs = new FileStream("Product Line Sales.rdl", FileMode.Open);
rpt.Load(fs, ".");
fs.Close();
rpt.ReportParameters["Category"].Value = 1;
rpt.ReportParameters["StartDate"].Value = DateTime.Now;
rpt.ReportParameters["EndDate"].Value = DateTime.Now.AddMonths(-1);
Rdl.Render.GenericRender render = rpt.Run();
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();
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.
<%@ 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:
protected void ReportParameters_ViewReport
(object sender, RdlAsp.ViewReportEventArgs e)
{
Rdl.Engine.Report rpt = e.Report;
Rdl.Render.GenericRender render = rpt.Run();
ReportViewer.SetReport(render);
}
protected void ProductLineSales_Click(object sender, EventArgs e)
{
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));
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.
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":
break;
default:
break;
}
}
private void ViewReport(object sender, RdlViewer.ParametersEntry.ViewReportEventArgs args)
{
Rdl.Render.GenericRender render = args.Report.Run();
reportViewer1.SetReport(render);
}
private void btnEmployeeSalesSummary_Click(object sender, EventArgs e)
{
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