Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Project RDL - Open Source Report Definition Language implementation in C#

0.00/5 (No votes)
9 Jan 2005 2  
The RDL Project is an open source (GPL) implementation of the RDL for the .NET environment created by fyiReporting Software. RDL is an XML based language created by Microsoft with the goal of promoting interoperability of reporting products. Project RDL is a compact implementation of RDL.

Simple Chart Report

Introduction

The RDL Project is an open source (GPL) C# implementation of the Report Definition Language (RDL) for the .NET environment, by fyiReporting Software.

RDL is an XML based language created by Microsoft with the goal of promoting interoperability of reporting products. Microsoft implemented RDL as part of the Microsoft SQL Server Reporting Services product. The RDL Project is a compact implementation of this powerful reporting language. It provides interoperability with Microsoft Reporting Services reports, letting you use reports in any of your .NET applications.

Warning: The download is an alpha version and should be used for experimentation purposes only. Later versions will be more fully tested and debugged.

Background

Overview of implemented RDL capabilities

The Report Definition Language (RDL) is a powerful and flexible language for defining reports. The specification for RDL runs over 100 pages. The RDL Project implements most of that functionality. While this article will barely touch the breadth of its capabilities, below is a list of some of the reporting capabilities that RDL enables:

Capability

Description

Charts

2D business graphics

  • Column (plain, stacked, percent stacked)
  • Bar (plain, stacked, percent stacked)
  • Line (plain, smooth)
  • Pie (plain, exploded)
  • Area (plain, stacked)
  • Doughnut

Tables

Presents report items in a tabular format

  • Table groups
  • Multiple header, footer, detail rows

List

Allows absolute positioning of report items

Matrix

Also known as cross tabulation.

  • Multi-level grouping of rows and columns

Output rendering

Reports can be rendered using multiple formats.

  • PDF
  • XML
  • .NET control
  • Printed reports

Datasources

Relational databases:

  • Microsoft SQL Server
  • Microsoft OLE DB Provider
  • ODBC - tested with MySQL

Expressions

Expression language based on VB.NET functions and expressions. Including the following:

Financial:

  • DDB, FV, IPmt, NPer, Pmt, PV, Rate, SLN, SYD

Aggregate:

  • sum, avg, count, max, min, stdev, stdevp, var, varp, running value of all aggregate functions

Control:

  • iif, choose, switch

.NET:

  • Math (Abs, Cos, Sin, Log, ...)
  • String (Concat, Format, IndexOf, Substring, ...)
  • Convert (ToDateTime, ToDouble, ToDecimal, ...)

Arithmetic:

  • +, -, /, *, ^, %
  • decimal and floating point arithmetic

Logical:

  • and, or, not

User written functions:

  • Anything you can imagine supporting both static and instance based methods.

What�s not there

The RDL Project doesn�t attempt to be API compatible with Microsoft SQL Server Reporting Services. The primary goal is that reports that run on Reporting Services will also work with the RDL Project. There are some limitations in the current alpha version. Two main absences are no 3D graphs and no subreport support. Again, please be aware that this is an alpha version that will require several months of testing and bug fixing before the reports generated can be fully trusted. Please stay tuned to future versions.

Modules

The RDL Project consists of a number of executables that provide (and demonstrate) various capabilities.

Component

Name

Description

RDL engine

RdlEngine.dll

Provides the reporting engine and rendering services. This is the base engine that all other components require.

.NET control

RdlViewer.dll

Provides a .NET control for embedding in .NET applications. Displays RDL reports, and provides methods for printing and saving to HTML, PDF, and XML.

RDL reader

RdlReader.exe

An MDI application that provides Adobe Reader like capabilities for RDL reports. This application shows some of the functionality supported by the .NET RDL control.

RDL designer

RdlDesigner.exe

An MDI application providing simple report creation, editing and previewing.

RDL desktop

RdlDesktop.exe

A small desktop report server providing browser access to your reports. Point your browser to the URL http://localhost:8080/. The port 8080 is the default and can be modified in the config.xml file. Also specified in the config file is the directory which contains the report files to be served.

RDL batch

RdlCmd.exe

Batch command executable for creating PDF, XML, HTML files from RDL files.

Sample RDL

The chart shown at the beginning of the article was created by the RDL listed below. As you can see, RDL (and XML, in general) is not especially compact. The RdlDesigner executable has some wizard support to automatically generate table and list reports from a database (SQL Server, MySQL via ODBC) but does not currently include a WYSIWYG designer. Fortunately, you can create RDL files using other tools, most notably Microsoft's designer that comes with Reporting Services, and run them using the RDL Project's reporting engine.

<?xml version='1.0' encoding='UTF-8'?>
<Report Name=''> 
    <PageHeight>8.5in</PageHeight>
    <PageWidth>11in</PageWidth>
    <LeftMargin>.5in</LeftMargin>
    <Description>Column Chart</Description>
    <Author>fyiReporting Software, LLC</Author>
    <DataSources>
        <DataSource Name='DS1'>
            <ConnectionProperties> 
                <DataProvider>SQL</DataProvider>
                change to this for sql server -->

Using the code

There are several ways that you can use and explore the RDL Project:

  1. Create RDL reports and use one of the executables (RdlReader, RdlDesigner, RdlCmd, RdlDesktop) to render the reports. This is the easiest way to become familiar with RDL and see if it provides the functionality you need for your reports.
  2. Embed the .NET control in your GUI application. To use the .NET control, you need to add a reference to the RdlViewer.dll and RdlEngine.dll in your project. You should then add RdlViewer.dll to your toolbox. Simply assigning a file name to the SourceFile property will cause your report to load. Alternatively, assign RDL XML to SourceRdl if you want to programmatically generate RDL and preview a report. Look at the code in the RdlReader project to see a simple example exploiting the printing, saving and other capabilities of the control.
  3. RdlCmd provides a simple example of how to directly call the reporting engine RdlEngine.dll. See the �Simple source example� below for a code example using this technique. One use for RdlCmd is to run reports in batch then upload the file(s) to your web site. This means that no report processing takes place dynamically from the web server lessening its load. Of course, this technique is only useful for reports whose rendered shelf life is fairly long (e.g., greater than 30 minutes).
  4. RdlDesktop provides a more complex example of how the RDL engine can be used in a multi-threaded environment. In general, a single report instance should be used by a single thread. Multiple threads can be used to allow multiple users to simultaneously render different reports. Caching of the rendered report (HTML, PDF, XML) allows greater throughput. This level of concurrency makes sense because the actual report is created only once and it is the rendered output that can be shared by multiple users.
  5. You might find various routines to be interesting in isolation. For example, the Financial.cs file in \RdlEngine\Functions contains static functions for the DDB, FV, IPmt, NPer, Pmt, PV, Rate, SLN and SYD functions.

Simple source example

The following code is a snippet from RdlCmd.cs that shows how to take a source report definition, compile it, obtain the data needed to create the report, and finally render it. Notice that the routine handles multiple input report definition files and can create one or more output renderings (HTML, PDF, and/or XML) from a single report.

      // Render the report files with the requested types 

      private void DoRender(string dir, string[] files, string[] types)
      {
            string source;
            Report report;
            int index;
            ListDictionary ld;
            string file;

            foreach (string filename in files)
            {
                  // Any parameters?  e.g.  file1.rdl?orderid=5 

                  index = filename.LastIndexOf('?');
                  if (index >= 0)
                  {
                        ld = this.GetParmeters(filename.Substring(index));
                        file = filename.Substring(0, index);
                  }
                  else 
                  {
                        ld = null;
                        file = filename;
                  }

                  // Obtain the source 

                  source = this.GetSource(file);
                  // GetSource is omitted: all it does is read the file.


                  if (source == null)
                        continue;  // error: process the rest of the files


                  // Compile the report 

                  report = this.GetReport(source, file);
                  if (report == null)
                        continue;  // error: process the rest of the files


                  // Obtain the data passing any parameters 

                  report.RunGetData(ld);

                  // Render the report in each of the requested types 

                  string fileNoExt;
                  fileNoExt = dir + Path.GetFileNameWithoutExtension(file);

                  foreach (string stype in types)
                  {
                        SaveAs(report, fileNoExt+"."+stype, stype);
                  }
            }     // end foreach files

      }

      // GetParameters creates a list dictionary

      // consisting of a report parameter name and a value.

      private ListDictionary GetParmeters(string parms)
      {
            ListDictionary ld= new ListDictionary();
            if (parms == null)
                  return ld; // dictionary will be empty in this case


            // parms are separated by & 

            char[] breakChars = new char[] {'&'};
            string[] ps = parms.Split(breakChars);

            foreach (string p in ps)
            {
                  int iEq = p.IndexOf("=");
                  if (iEq > 0)
                  {
                        string name = p.Substring(0, iEq);
                        string val = p.Substring(iEq+1);
                        ld.Add(name, val);
                  }
            }
            return ld;
      }

      private Report GetReport(string prog, string file)
      {
            // Now parse the file 

            RDLParser rdlp;
            Report r;

            try 
            {
                  rdlp =  new RDLParser(prog);
                  // RDLParser takes RDL XML and Parse compiles the report


                  r = rdlp.Parse();
                  if (r.rl.MaxSeverity > 0) 
                  {
                        // have errors fill out the msgs 

                        Console.WriteLine("{0} has the following errors:", file);

                        foreach (string emsg in r.rl.ErrorItems)
                        {
                              Console.WriteLine(emsg);
                              // output message to console

                        }

                        int severity = r.rl.MaxSeverity;
                        r.rl.Reset();
                        if (severity > 4)
                        {
                              r = null; // don't return when severe errors

                              returnCode = 8;
                        }
                  }
            }
            catch(Exception e)
            {
                  r = null;
                  Console.WriteLine(e.Message);
                  returnCode = 8;
            }

            return r;
      }

      // Save the file.  The extension determines the type of file to save. 

      // "FileName" Name of the file to be saved to. 

      // "ext" Type of file to save.  Should be "pdf", "xml", "html". 

      private void SaveAs(Report report, string FileName, string type)
      {
            string ext = type.ToLower();
            OneFileStreamGen sg=null;

            try 
            {
                  sg = new OneFileStreamGen(FileName, true);
                  // overwrite with this name


                  switch(ext)
                  {
                        case "pdf":
                          report.RunRender(sg, OutputPresentationType.PDF);
                          break;
                        case "xml": 
                          report.RunRender(sg, OutputPresentationType.XML);
                          break;
                        case "html": case "htm":
                          report.RunRender(sg, OutputPresentationType.HTML);
                          break;
                        default:
                          Console.WriteLine("Unsupported file " + 
                            "extension '{0}'.  " + 
                            "Must be 'pdf', 'xml' or 'html'", type);
                          returnCode = 8;
                          break;
                  }
            }

            catch(Exception e)
            {
                  Console.WriteLine(e.Message);
                  returnCode = 8;
            }
            finally 
            {
                  if (sg != null)
                  {
                        sg.CloseMainStream();
                  }
            }
            return;
      }

Points of Interest

This article touched on only a small part of the functionality in the RDL Project. If reporting (or printing) is one of the requirements for your applications, RDL may be a good solution for you. I hope you find the RDL Project useful for your own presentation needs. Please contact me with any comments you have.

References

  • The home of the RDL project - You�ll find the latest versions of the software at this site. In addition to the source, a Microsoft installation is available for installing just the binary modules. The software is updated on roughly a monthly basis.
  • The specification for RDL - This is the definitive definition of the language. It is well written, but like most specifications is not really good for learning the language.
  • Search for Reporting Services books - There are quite a few books written about Microsoft SQL Server Reporting Services. Unfortunately, most of these books don�t go into much detail about RDL syntax.

History

January, 2005 - first alpha version of the RDL Project is placed on the web.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here