前一阵子工作中遇到这么一个问题,需要检查几十个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
小结:
用类似的方法,可以操作任意的报表元素,甚至可以从头开始创建一个完整的报表。但是可以看出这种方法相对繁琐,而且容易出错,如果需要大批量处理报表中的少量元素,还是不失为一种好方法。