Editing RDL programatically using C#

Recently i was working on an issue.

In Reporting Service 2005 you set FixedHeader property to a table control. When the dataset bound with the table returns no data in combination with Internet Explorer -> Tools -> Internet Options -> Advanced -> Uncheck Disable Script Debugging (Internet Explorer), you'll receive a Javascript error.

---------------------------
Error
---------------------------
A Runtime Error has occurred.
Do you wish to Debug?

Line: 12
Error: Object required
---------------------------
Yes No
---------------------------

This happens with both IE 6 and IE 7.

WORKAROUND:
=============

1. Set the No Rows property of the table with some value like "Sorry no data returned"
                       OR
2. Add the expression for the visiblity of the table which holds the data.
               =IIF((RowNumber("DataSet")=0),True,False)

But consider the situation where you've 100's of reports already deployed in the report server. You'll not be in favor of editing each and every report and re deploy the same.

For that i wrote a piece of C# code which will read each and every deployed report from the report server, scan through the table element and where ever the FixedHeader property is set, it inserts an element <NoRows> and puts the report defintion back at the report server. I've tested the code and it works great.

CODE:
=====

Below is a console application code written in C# which accepts two parameters, one the complete Report Server URL and the other is the message that need to displayed when there are No Rows. (You can leave it blank as well)

Like this: somename.exe "https://machinename/reportserver" "Query didn't return any data "

I've commented the code for easier understanding.

Additional references,

using System.Xml;
using System.Xml.XPath;
using System.IO;

Make sure to add a Web Reference for ReportService.asmx (2005 end point) and include it in the project with the using statement.

class Program
{
static void Main(string[] args)
{
if (args.Length == 2)//Make sure we pass exactly two parameters.
{
try
{
ReportingService rs = new ReportingService();
int totalReports = 0; //To keep track of number of reports that were processed.
int editedReports = 0; //To keep track of number of reports that were edited.

                    rs.Url = args[0] + "/ReportService.asmx"; //First command line argument, takes the appropriate Report Server URL.
rs.Credentials = System.Net.CredentialCache.DefaultCredentials;

                    //Retrieving the list of all the items deployed in the Report Server.
CatalogItem[] catalogitems = rs.ListChildren("/", true);

                    //Looping through each and every item.
foreach (CatalogItem item in catalogitems)
{
//We're concerned only about the reports and thats were the processing starts.
if (ItemTypeEnum.Report == item.Type)
{
bool rdlModified = false; //To make sure only the modified RDL's are written back.
bool isFixedHeader = false; //To make sure we edit only the RDL's that has Fixed Headers.
string reportPath = item.Path; //Path of the report that is currently being processed.
byte[] reportDefinition = null; //Holds the Original RDL byte array.
byte[] newReport = null; //Holds the Modified RDL byte array from the stream.
XmlDocument xmldoc = null; //To hold the RDL in the XML form.

                            reportDefinition = rs.GetReportDefinition(reportPath);

                            Console.WriteLine("Processing the report: " + reportPath);
totalReports++;

                            using (MemoryStream memOriginalRDL = new MemoryStream(reportDefinition))
{
xmldoc = new XmlDocument();
xmldoc.Load(memOriginalRDL);
memOriginalRDL.Close();
}

                            XmlNode root = xmldoc.DocumentElement;
XmlNodeList nodelist = root.SelectNodes("descendant::*"); //To make sure we've all the elements under the root are traversed.

                            foreach (XmlNode node in nodelist)
{
if (node.Name == "Table") //We're only concerned about table.
{
int count = 0;
XmlNodeList childList = node.ChildNodes;

                                    foreach (XmlNode childnode in childList)
{
if (childnode.Name == "NoRows") //Checking if there is NoRows element already present.
{
count++;
}

                                        if (childnode.Name == "Header") //Setting the flag if only we've the Fixed Header enabled.
{
XmlNodeList headerChildList = childnode.ChildNodes;

                                            foreach (XmlNode headernode in headerChildList)
{
if (headernode.Name == "FixedHeader")
{
isFixedHeader = true;
}
}
}
}

                                    if (count == 0 && isFixedHeader) //Only if NoRows is not available, proceed with the RDL edit.
{
Console.WriteLine("\t Editing the report: " + reportPath);

                                        XmlElement newElement = xmldoc.CreateElement("NoRows", root.NamespaceURI.ToString());
newElement.InnerText = args[1].ToString(); //Second argument from the commandline, customized message to display.
node.AppendChild(newElement);
rdlModified = true;
}
}
}

                            if (rdlModified)//If only RDL is modified.
{
//Get the XML back to memory stream and Convert the memory stream in to byte array.
using (MemoryStream memModifiedRDL = new MemoryStream())
{
xmldoc.Save(memModifiedRDL);
newReport = memModifiedRDL.GetBuffer();
memModifiedRDL.Close();
}

//Put it back in to Report Server database.
ReportService2K5.Warning[] warning = rs.SetReportDefinition(item.Path, newReport);
editedReports++;

                                Console.WriteLine("\t finished editing the report: " + reportPath);
}
}
}
Console.WriteLine("\n\n================================");
Console.WriteLine("Completed the Processing of {0} reports. Edited {1} reports. Hit enter to QUIT.",totalReports,editedReports);
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine("Exception occured: " + ex.Message.ToString());
}
}
else
{
Console.WriteLine("Please run the EXE from the command line with the ReportServer URL like : EditRDLProgramatically.exe 'https://machinename/reportserver' 'QUERY doesn't return any data'");
Console.ReadLine();
}
}
}