When you develop a new report for TFS and you want to deploy the report to all team projects, you have to do a lot of work to upload the file and to set the datasources.
There is an easy way to automate that.
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Xml;
using UploadLibrary.ReportService2005;
namespace UploadLibrary
{
public class Report
{
public Report(string[] localFiles)
{
LocalFiles = localFiles;
}
public string[] LocalFiles { get; set; }
public void Upload()
{
foreach (var subFolder in SubFolders())
{
foreach (var localFile in LocalFiles)
{
Upload(subFolder, localFile);
}
}
}
private void Upload(string subFolder, string file)
{
var fi = new FileInfo(file);
string reportName = fi.Name.Substring(0, fi.Name.Length - fi.Extension.Length);
string reportPath = "/" + subFolder + "/" + reportName;
var rs = new ReportingService2005 { Credentials = CredentialCache.DefaultCredentials };
//Read the file from the file system
FileStream stream = File.OpenRead(file);
var bytes = new byte[stream.Length];
stream.Read(bytes, 0, (int)stream.Length);
stream.Close();
//Search for an exiting item in the subfolders (recursively)
CatalogItem[] items = rs.FindItems("/" + subFolder,
BooleanOperatorEnum.And,
new[]
{
new SearchCondition
{
Name = "Name",
Condition = ConditionEnum.Equals,
Value = reportName
}
});
// Only add the report in the current subfolder
if (items.Count(c => c.Path == reportPath) > 0)
{
//Report exists -> update the report
rs.SetReportDefinition(reportPath, bytes);
}
else
{
//Report does not exist -> create a new report
rs.CreateReport(reportName, "/" + subFolder, false, bytes, new Property[0]);
}
//Relocate the datasources to the parent folder
rs.SetItemDataSources(reportPath, UsedDatasources(file));
}
private static string[] SubFolders()
{
var rs = new ReportingService2005 { Credentials = CredentialCache.DefaultCredentials };
var items = rs.ListChildren("/", false);
return items.Where(i => i.Type == ItemTypeEnum.Folder).Select(i => i.Name).ToArray();
}
/// <summary>
/// Returns an array of all datasources that are in use by the report.
/// </summary>
private DataSource[] UsedDatasources(string file)
{
var datasources = new Collection<DataSource>();
// Read the rdl file
var doc = new XmlDocument();
doc.Load(file);
var nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("rdl", "http://schemas.microsoft.com/sqlserver/reporting/2005/01/reportdefinition");
// Go to the datasources node in the rdl file
var nodes = doc.SelectNodes("rdl:Report/rdl:DataSources/rdl:DataSource", nsmgr);
if (nodes != null)
{
// Iterate through the datasources in the rdl file
foreach (XmlNode node in nodes)
{
// Read the name of the datasource
var datasourceName = node.Attributes["Name"].InnerText;
// Add it to the result
datasources.Add(new DataSource
{
Item = new DataSourceReference { Reference = "/" + datasourceName },
Name = datasourceName
});
}
}
// Return the result
return datasources.ToArray();
}
}
}
You can add a winform project to your solution. On the form add a button called uploadReport and a textbox called fileName
private void uploadReport_Click(object sender, EventArgs e)
{
Cursor = Cursors.WaitCursor;
try
{
//Validation
if (!File.Exists(fileName.Text))
{
MessageBox.Show(this, "Invalid filename", "Validation", MessageBoxButtons.OK,
MessageBoxIcon.Error);
return;
}
//Start upload
var rpt = new Report(new string[] { fileName.Text });
rpt.Upload();
//Successful upload.
MessageBox.Show(this, "The report is uploaded to all folders on the server!", "Successful", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show(this, ex.Message, "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
Cursor = Cursors.Default;
}
}