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






4.83/5 (26投票s)
2005年1月10日
7分钟阅读

370659

7869
RDL Project 是由 fyiReporting Software 为 .NET 环境创建的 RDL 的开源(GPL)实现。RDL 是 Microsoft 创建的一种基于 XML 的语言,其目标是促进报表产品的互操作性。Project RDL 是 RDL 的紧凑实现。
引言
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 |
二维商业图表
|
表格 |
以表格格式呈现报表项
|
列表 |
允许报表项的绝对定位 |
矩阵 |
也称为交叉表。
|
输出渲染 |
报表可以使用多种格式进行渲染。
|
数据源 |
关系数据库
|
表达式 |
基于 VB.NET 函数和表达式的表达式语言。包括以下内容: 金融
Aggregate
Control
.NET
算术运算
逻辑
用户编写的函数
|
缺失的功能
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:
- 创建 RDL 报表,并使用其中一个可执行文件(RdlReader、RdlDesigner、RdlCmd、RdlDesktop)来渲染报表。这是熟悉 RDL 并查看其是否提供所需功能的报表功能的简便方法。
- 将 .NET 控件嵌入到您的 GUI 应用程序中。要使用 .NET 控件,您需要在项目中添加对 RdlViewer.dll 和 RdlEngine.dll 的引用。然后,您应该将 RdlViewer.dll 添加到工具箱中。只需将文件名赋给
SourceFile
属性,即可加载您的报表。或者,如果您想以编程方式生成 RDL 并预览报表,则将 RDL XML 赋给 SourceRdl。查看 RdlReader 项目中的代码,了解一个利用控件的打印、保存和其他功能的简单示例。 - RdlCmd 提供了一个简单的示例,展示如何直接调用报表引擎 RdlEngine.dll。请参阅下面的“简单源代码示例”,了解使用此技术的代码示例。RdlCmd 的一个用途是批量运行报表,然后将文件上传到您的网站。这意味着报表处理不会在 Web 服务器上动态进行,从而减轻了 Web 服务器的负载。当然,这种技术仅适用于渲染后的保质期较长的报表(例如,大于 30 分钟)。
- RdlDesktop 提供了 RDL 引擎如何在多线程环境中使用的更复杂的示例。通常,单个报表实例应由单个线程使用。可以使用多个线程来允许多个用户同时渲染不同的报表。渲染后的报表(HTML、PDF、XML)的缓存可以提高吞吐量。这种级别的并发性是有意义的,因为实际报表仅创建一次,而渲染后的输出可以由多个用户共享。
- 您可能会发现各种例程本身就很有趣。例如,\RdlEngine\Functions 目录下的 Financial.cs 文件包含
DDB
、FV
、IPmt
、NPer
、Pmt
、PV
、Rate
、SLN
和SYD
函数的静态函数。
简单源代码示例
以下代码是 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 版本上线。