65.9K
CodeProject 正在变化。 阅读更多。
Home

Project RDL - C# 中的开源报告定义语言实现

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.83/5 (26投票s)

2005年1月10日

7分钟阅读

viewsIcon

370659

downloadIcon

7869

RDL Project 是由 fyiReporting Software 为 .NET 环境创建的 RDL 的开源(GPL)实现。RDL 是 Microsoft 创建的一种基于 XML 的语言,其目标是促进报表产品的互操作性。Project RDL 是 RDL 的紧凑实现。

Simple Chart Report

引言

RDL Project 是由 fyiReporting Software 为 .NET 环境开发的、开源(GPL)的 C# 版报表定义语言(RDL)实现。

RDL 是 Microsoft 创建的一种基于 XML 的语言,其目标是促进报表产品的互操作性。Microsoft 将 RDL 作为 Microsoft SQL Server Reporting Services 产品的一部分来实现。RDL Project 是这种强大报表语言的紧凑实现。它提供了与 Microsoft Reporting Services 报表的互操作性,使您可以在任何 .NET 应用程序中使用报表。

警告:此下载版本为 Alpha 版,仅应用于实验目的。后续版本将经过更全面的测试和调试。

背景

已实现 RDL 功能概述

报表定义语言(RDL)是一种功能强大且灵活的报表定义语言。RDL 的规范长达 100 多页。RDL Project 实现 了其中大部分功能。虽然本文仅能略微触及 RDL 功能的广度,但以下是 RDL 实现的一些报表功能列表:

功能

描述

Charts

二维商业图表

  • 柱形图(普通、堆积、百分比堆积)
  • 条形图(普通、堆积、百分比堆积)
  • 折线图(普通、平滑)
  • 饼图(普通、分离)
  • 面积图(普通、堆积)
  • 圆环图

表格

以表格格式呈现报表项

  • 表格分组
  • 多个页眉、页脚、详细信息行

列表

允许报表项的绝对定位

矩阵

也称为交叉表。

  • 多级行和列分组

输出渲染

报表可以使用多种格式进行渲染。

  • PDF
  • XML
  • .NET 控件
  • 打印报表

数据源

关系数据库

  • Microsoft SQL Server
  • Microsoft OLE DB 提供程序
  • ODBC - 已与 MySQL 测试

表达式

基于 VB.NET 函数和表达式的表达式语言。包括以下内容:

金融

  • DDBFVIPmtNPerPmtPVRateSLNSYD

Aggregate

  • sumavgcountmaxminstdevstdevpvarvarp,以及所有聚合函数的累加值

Control

  • iifchooseswitch

.NET

  • MathAbsCosSinLog 等)
  • StringConcatFormatIndexOfSubstring 等)
  • ConvertToDateTimeToDoubleToDecimal 等)

算术运算

  • +, -, /, *, ^, %
  • 十进制和浮点数运算

逻辑

  • andornot

用户编写的函数

  • 您能想到的任何支持静态和实例方法的函数。

缺失的功能

RDL Project 不试图实现与 Microsoft SQL Server Reporting Services 的 API 兼容。主要目标是能够在 Reporting Services 上运行的报表也能在 RDL Project 中运行。当前的 Alpha 版本存在一些限制。两个主要缺失的功能是 3D 图形和子报表支持。请注意,这是一个 Alpha 版本,需要数月的测试和错误修复才能完全信任生成的报表。请持续关注后续版本。

模块

RDL Project 包含多个可执行文件,它们提供了(并演示了)各种功能。

组件 (Component)

名称

描述

RDL 引擎

RdlEngine.dll

提供报表引擎和渲染服务。这是所有其他组件都需要的底层引擎。

.NET 控件

RdlViewer.dll

提供一个 .NET 控件,可嵌入到 .NET 应用程序中。显示 RDL 报表,并提供打印和保存为 HTML、PDF 和 XML 的方法。

RDL 读取器

RdlReader.exe

一个 MDI 应用程序,提供类似 Adobe Reader 的 RDL 报表功能。此应用程序展示了 .NET RDL 控件支持的一些功能。

RDL 设计器

RdlDesigner.exe

一个 MDI 应用程序,提供简单的报表创建、编辑和预览功能。

RDL 桌面

RdlDesktop.exe

一个小型桌面报表服务器,提供对报表的浏览器访问。将您的浏览器指向 URL https://:8080/。端口 8080 是默认端口,可以在 config.xml 文件中修改。配置文件中还指定了包含要服务的报表文件的目录。

RDL 批量处理

RdlCmd.exe

批量命令可执行文件,用于从 RDL 文件创建 PDF、XML、HTML 文件。

示例 RDL

文章开头显示的图表是由下面的 RDL 创建的。可以看到,RDL(以及 XML,总的来说)并不特别紧凑。RdlDesigner 可执行文件提供了一些向导支持,可以从数据库(SQL Server、MySQL 通过 ODBC)自动生成表格和列表报表,但目前不包含所见即所得的设计器。幸运的是,您可以使用其他工具创建 RDL 文件,最值得注意的是 Microsoft 提供的 Reporting Services 中的设计器,然后使用 RDL Project 的报表引擎运行它们。

<?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 -->
                <ConnectString>
                  Server=(local)\VSDotNet;DataBase=Northwind;Integrated 
                  Security=SSPI;Connect Timeout=5</ConnectString>
            </ConnectionProperties>
        </DataSource>
    </DataSources>
    <DataSets>
        <DataSet Name='Data'>
            <Query>
                <DataSourceName>DS1</DataSourceName>
                <CommandText>SELECT Category, Year, 
                       Sales FROM CategorySalesView</CommandText>
            </Query>
            <Fields>
                <Field Name='Category'> 
                    <DataField>Category</DataField>
                </Field>
                <Field Name='Year'> 
                    <DataField>Year</DataField>
                </Field>
                <Field Name='Sales'> 
                    <DataField>Sales</DataField>
                </Field>
            </Fields>
        </DataSet>
    </DataSets>
<Body>
  <ReportItems>
     <Chart Name="column_chart">
        <Height>7 in</Height>
        <Width>10 in</Width>
        <Style>
          <BackgroundGradientEndColor>Gray</BackgroundGradientEndColor>
          <BackgroundGradientType>LeftRight</BackgroundGradientType>
          <BackgroundColor>White</BackgroundColor>
        </Style>
        <Legend>
          <Visible>true</Visible>
          <Style>
            <WritingMode>lr-tb</WritingMode>
            <BorderStyle>
              <Default>None</Default>
            </BorderStyle>
            <BorderWidth><Default>1pt</Default></BorderWidth>
            <TextAlign>Left</TextAlign>
          </Style>
          <Position>BottomCenter</Position>
          <Layout>Row</Layout>
        </Legend>
        <Palette>Default</Palette>
        <ChartData>
          <ChartSeries>
            <DataPoints>
              <DataPoint>
                <DataValues>
                  <DataValue>
                    <Value>=Sum(Fields!Sales.Value)</Value>
                  </DataValue>
                </DataValues>
                <DataLabel>
                  <Style>
                    <Format>$#,###</Format>
                    <Color>Red</Color>
                    <VerticalAlign>Middle</VerticalAlign>
                  </Style>
                  <Visible>true</Visible>
                </DataLabel>
                <Marker />
              </DataPoint>
            </DataPoints>
          </ChartSeries>
        </ChartData>
        <CategoryAxis>
          <Axis>
            <MajorGridLines>
              <Style>
                <BorderStyle>
                  <Default>Solid</Default>
                </BorderStyle>
              </Style>
            </MajorGridLines>
            <MinorGridLines>
              <Style>
                <BorderStyle>
                  <Default>Solid</Default>
                </BorderStyle>
              </Style>
            </MinorGridLines>
            <MajorInterval>1</MajorInterval>
            <MinorInterval>1</MinorInterval>
            <Margin>true</Margin>
            <Visible>true</Visible>
            <Scalar>true</Scalar>
            <Title>
              <Caption>Years</Caption>
              <Style>
                <FontWeight>Bolder</FontWeight>
                <FontSize>20pt</FontSize> 
                <FontStyle>Normal</FontStyle>
              </Style>
            </Title>
            <Style>
               <FontWeight>Bolder</FontWeight>
              <FontSize>15pt</FontSize> 
              <FontStyle>Italic</FontStyle>
            </Style>
            <MajorTickMarks>Cross</MajorTickMarks>
          </Axis>
        </CategoryAxis>
        <DataSetName>Data</DataSetName>
        <Type>Column</Type>
        <Top>0.375in</Top>
        <PageBreakAtEnd>true</PageBreakAtEnd>
        <Title>
          <Caption>Sales by Category</Caption>
          <Style>
            <WritingMode>rl-tb</WritingMode>
            <FontWeight>Bolder</FontWeight>
            <FontSize>20pt</FontSize> 
            <FontStyle>Normal</FontStyle>
          </Style>
        </Title>
        <CategoryGroupings>
          <CategoryGrouping>
            <DynamicCategories>
              <Grouping Name="column_chart_CategoryGroup">
                <GroupExpressions>
                  <GroupExpression>=Fields!Year.Value</GroupExpression>
                </GroupExpressions>
              </Grouping>
              <Label>=Fields!Year.Value</Label>
            </DynamicCategories>
          </CategoryGrouping>
        </CategoryGroupings>
        <SeriesGroupings>
          <SeriesGrouping>
            <DynamicSeries>
              <Grouping Name="column_chart_SeriesGroup">
                <GroupExpressions>
                  <GroupExpression>=Fields!Category.Value</GroupExpression>
                </GroupExpressions>
              </Grouping>
              <Label>=Fields!Category.Value</Label>
            </DynamicSeries>
          </SeriesGrouping>
        </SeriesGroupings>
        <Subtype>PercentStacked</Subtype>
        <PlotArea>
          <Style>
            <BorderStyle>
              <Default>Solid</Default>
            </BorderStyle>
            <BackgroundGradientEndColor>White</BackgroundGradientEndColor>
            <BackgroundGradientType>DiagonalRight</BackgroundGradientType>
            <BackgroundColor>Red</BackgroundColor>
          </Style>
        </PlotArea>
        <ValueAxis>
          <Axis>
            <Title>
              <Caption>Percentages</Caption>
              <Style>
                <WritingMode>tb-rl</WritingMode>
                <FontWeight>Bolder</FontWeight>
                <FontSize>20pt</FontSize> 
                <FontStyle>Normal</FontStyle>
              </Style>
            </Title>
            <Style>
              <Format>0%</Format>
            </Style>
            <MajorGridLines>
              <ShowGridLines>true</ShowGridLines>
              <Style>
                <BorderStyle>
                  <Default>Solid</Default>
                </BorderStyle>
                <BorderWidth>
                  <Default>1pt</Default>
                </BorderWidth>
              </Style>
            </MajorGridLines>
            <MinorGridLines>
              <Style>
                <BorderStyle>
                  <Default>Solid</Default>
                </BorderStyle>
              </Style>
            </MinorGridLines>
            <Visible>true</Visible>
            <MajorTickMarks>Cross</MajorTickMarks>
          </Axis>
        </ValueAxis>
      </Chart>
  </ReportItems>
</Body>
</Report>

使用代码

有几种方法可以您使用和探索 RDL Project:

  1. 创建 RDL 报表,并使用其中一个可执行文件(RdlReader、RdlDesigner、RdlCmd、RdlDesktop)来渲染报表。这是熟悉 RDL 并查看其是否提供所需功能的报表功能的简便方法。
  2. 将 .NET 控件嵌入到您的 GUI 应用程序中。要使用 .NET 控件,您需要在项目中添加对 RdlViewer.dllRdlEngine.dll 的引用。然后,您应该将 RdlViewer.dll 添加到工具箱中。只需将文件名赋给 SourceFile 属性,即可加载您的报表。或者,如果您想以编程方式生成 RDL 并预览报表,则将 RDL XML 赋给 SourceRdl。查看 RdlReader 项目中的代码,了解一个利用控件的打印、保存和其他功能的简单示例。
  3. RdlCmd 提供了一个简单的示例,展示如何直接调用报表引擎 RdlEngine.dll。请参阅下面的“简单源代码示例”,了解使用此技术的代码示例。RdlCmd 的一个用途是批量运行报表,然后将文件上传到您的网站。这意味着报表处理不会在 Web 服务器上动态进行,从而减轻了 Web 服务器的负载。当然,这种技术仅适用于渲染后的保质期较长的报表(例如,大于 30 分钟)。
  4. RdlDesktop 提供了 RDL 引擎如何在多线程环境中使用的更复杂的示例。通常,单个报表实例应由单个线程使用。可以使用多个线程来允许多个用户同时渲染不同的报表。渲染后的报表(HTML、PDF、XML)的缓存可以提高吞吐量。这种级别的并发性是有意义的,因为实际报表仅创建一次,而渲染后的输出可以由多个用户共享。
  5. 您可能会发现各种例程本身就很有趣。例如,\RdlEngine\Functions 目录下的 Financial.cs 文件包含 DDBFVIPmtNPerPmtPVRateSLNSYD 函数的静态函数。

简单源代码示例

以下代码是 RdlCmd.cs 的一个代码片段,展示了如何获取源报表定义、编译它、获取创建报表所需的数据,最后进行渲染。请注意,该例程处理多个输入报表定义文件,并且可以从单个报表中创建一种或多种输出渲染(HTML、PDF 和/或 XML)。

      // 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;
      }

关注点

本文仅涉及了 RDL Project 功能的一小部分。如果报表(或打印)是您应用程序的要求之一,RDL 可能是个不错的解决方案。希望您发现 RDL Project 对您自己的演示需求很有用。如有任何意见,请通过 联系。

参考文献

  • RDL 项目主页 - 您可以在此网站找到最新版本的软件。除了源代码,还有 Microsoft 安装程序可用于仅安装二进制模块。软件大约每月更新一次。
  • RDL 规范 - 这是该语言的权威定义。它写得很好,但像大多数规范一样,它并不适合学习该语言。
  • 搜索 Reporting Services 书籍 - 有相当多的书籍写了关于 Microsoft SQL Server Reporting Services 的内容。不幸的是,其中大部分书籍都没有详细介绍 RDL 语法。

历史

2005 年 1 月 - RDL Project 的第一个 Alpha 版本上线。

© . All rights reserved.