前一阵子工作中遇到这么一个问题,需要检查几十个RDLC报表,判断其中是否存在几个报表参数,若不存在则进行追加。当时我是纯手工处理的,以记事本打开报表,检查有没有那几个参数,没有的话就手工添加进去一些XML片段。
在Visual Studio 2010 Ultimate RC 下测试通过
在RDLC报表中,报表参数由类似下面的XML进行描述:
……
<ReportParameters>
<ReportParameter Name="p_Country">
<DataType>String</DataType>
<Prompt>ReportParameter1</Prompt>
</ReportParameter>
<ReportParameter Name="p_Address">
<DataType>String</DataType>
<Prompt>ReportParameter1</Prompt>
</ReportParameter>
……
</ReportParameters>
……
RDL报表与RDLC报表是由RDL语言描述的,RDL = Report Definition Language 报表定义语言。
RDL规范至今已有四个版本:
Report server version | RDL schema version |
SQL Server 2008 R2 | 2000 RDL |
2005 RDL | |
2008 RDL | |
2009 RDL | |
SQL Server 2008 | 2000 RDL |
2005 RDL | |
2008 RDL |
其中最新版本2009 RDL将于2010年5月随同SQL Server 2008 R2 Reporting Services一起发布。由于RDL是基于XML的描述性语言,所以可以像操作XML那样来操作RDL,进而操作报表中的元素。我将使用Linq to XML来操作RDL,解决前面修改报表参数的问题。
首先,我们来看下Visual Studio 2010中对RDLC报表支持的改变。
Visual Studio 2008中的RDLC的命名空间是这样的:
<Report xmlns="http://schemas.microsoft.com/sqlserver/reporting/2005/01/reportdefinition" xmlns:rd="http://schemas.microsoft.com/SQLServer/reporting/reportdesigner">
在Visual Studio 2010中,命名空间改变如下:
<Report xmlns:rd="http://schemas.microsoft.com/SQLServer/reporting/reportdesigner" xmlns="http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition">
如果在VS 2010中打开之前版本的报表,会提示自动转换。奇怪的是VS 2010中没有采用RDL 2009规范,不知道等SQL Server 2008 R2正式发布后会不会更新。
此外,VS 2010中的RDLC设计界面也发生了变化,清一色的Reporting Services风格:
工具箱中多了一个仪表控件,可以实现指针效果:
针对即将发布的SQL Server 2008 R2,微软也提供了Report Builder 3.0的测试版,可以在这里下载。注意:必须要先安装SQL Server 2008 R2才能安装Report Builder 3.0,此外还需要.NET Framework 3.5 With SP1。Report Builder 3.0 提供了新的控件:Map、Data Bar、SparkLine,提供更加友好的用户体验:
遗憾的是Report Builder 3.0依然不支持RDLC的编辑。
下面我们进入正题,以VS 2010中的RDL 2008规范为例进行测试。
创建一个RDLC报表,添加两个报表参数:p_Country、p_Address。
以记事本打开RDLC报表,可以看到如下元素:
<Report xmlns:rd="http://schemas.microsoft.com/SQLServer/reporting/reportdesigner" xmlns="http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition">
......
<ReportParameter Name="p_Country">
<DataType>String</DataType>
<Prompt>ReportParameter1</Prompt>
</ReportParameter>
<ReportParameter Name="p_Address">
<DataType>String</DataType>
<Prompt>ReportParameter1</Prompt>
</ReportParameter>
......
有两个命名空间,一个前缀为rd,另一个是默认的命名空间。要使用Linq to XML操作RDL语言,首先要将其放到内存中:


{
FileStream file = new FileStream(v_strPath, FileMode.Open, FileAccess.ReadWrite);
XmlReader reader = XmlReader.Create(file);
XElement rdlc = XElement.Load(reader);
下面要将两个命名空间添加进来:


XmlNamespaceManager mgr = new XmlNamespaceManager(nameTable);
mgr.AddNamespace( " rd " , " http://schemas.microsoft.com/SQLServer/reporting/reportdesigner " );
mgr.AddNamespace( " x " , " http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition " );
注意,对于默认的命名空间,需要随意指定一个前缀,否则待会用Linq查询不到元素。
然后判断指定的参数是否存在,若不存在则进行添加:


<ReportParameter Name="p_City">
<DataType>String</DataType>
<Prompt>p_City</Prompt>
</ReportParameter>
*/
IEnumerable < XElement > report = from r in rdlc.XPathSelectElements( @" //x:ReportParameters/x:ReportParameter " , mgr)
select r;
bool __bol = false ;
foreach (var r in report)
{
if (r.Attribute( " Name " ).Value == " p_City " )
{
__bol = true ;
}
}
if ( ! __bol)
{
report.First().AddAfterSelf( new XElement( " {http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition}ReportParameter " , new XAttribute( " Name " , " p_City " ),
new XElement( " {http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition}DataType " , " String " ),
new XElement( " {http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition}Prompt " , " p_City " )));
}
注意这里不能用刚才随意添加的那个前缀“x”了,需要指定默认命名空间的全称,用大括号括起来。
最后释放占用的资源,保存所做的更改:
file.Close();
rdlc.Save(v_strPath);
此时打开报表后会看到我们刚才添加的参数:p_City
小结:
用类似的方法,可以操作任意的报表元素,甚至可以从头开始创建一个完整的报表。但是可以看出这种方法相对繁琐,而且容易出错,如果需要大批量处理报表中的少量元素,还是不失为一种好方法。