Background Story

In this fictional example, we have a SQL Server/SSRS Server safe behind our firewall, and a web server in the DMZ.  Our web server hosts a Silverlight application our field sales team uses for performance analysis.

For this sample, we'll create a scheduled report, which will be saved on a network share.  For simplicity, we'll save the files directly to the web server, although we could place these reports anywhere that can be accessed with network credentials.  We'll create a simple web service, which will provide a list of reports to our application, and deliver the requested report.

To perform this full demo on your own system, you'll need an SSRS report server in place.  If you don't have that, a folder with some useful PDFs will suffice-just skip the next section.

The end result of what we'll accomplish can be seen at http://demo.componentone.com/silverlight/reportviewer/.  We're going to build most of this demo (PDF part only) in this post, the rest can be seen in the sample code.  The sample code and pre-release bits can be downloaded via the announcement post at http://helpcentral.componentone.com/CS/silverlight_161/f/191/t/86680.aspx.

Scheduling the Report

Log in to your report server at http://[myserver]/reports/, and select the report you want to schedule.  The report will open, and you should see a New Subscription button in the chrome above the report.  If the chrome isn't present, check for a Subscriptions tab.  If that's not present, check with your SSRS administrator to have your permissions adjusted.

image

On the subscription page, we set the Report Delivery Options.  It's simplest to share a folder from the web app, but that may not be the best architecture in your given environment.  The most important setting below may be the credentials-this is where a lot of issues arise.  These credentials will be used by SSRS to place the report on the file share.  It's important to use a domain account, and to make sure that account has at least MODIFY permissions on the destination folder.  A best practice is to create an account specifically for this purpose.

image

We can also choose the schedule our report is created.

image

After setting the schedule, click OK.  When you get back to the report, it's a good idea to generate a PDF so we have a test file for building the WCF Service.

Building the WCF Service

Depending on your system architecture, you could either have the WCF and Silverlight apps in separate solutions, or put both in the same solution.  Here, we'll do both in the same solution because it's a little easier for demo purposes.

Start by creating a new Silverlight Application, targeting Silverlight 4.  I've called my solution ReportViewerQuickStart.
image

Since we're creating the WCF and Silverlight project in the same solution, we can choose to host our Silverlight app in a new web application, and add our service to that web application.

image

When we create a Silverlight application in this manner, our solution contains two projects.  One is the Silverlight project (named for the solution), and a web application called ReportViewerQuickStart.Web.  ReportViewerQuickStart.Web is what will host our WCF service.

image

To the web app, add a Resources folder.  This is the folder we'll share to SSRS to save the reports in.  Add our sample PDF report to this folder.

image

Add a new WCF Service to the web app; I've named it ReportingService.svc.

image

We need two operations for our sample-GetReportList(), which will provide the list of reports in the source folder to the client, and GetReportStream(), which will deliver the specific report to the client.  In ReportingService.svc.cs, we need to add a "using" statement for System.IO, and implement GetReportList and GetReportStream (the complete file is found in the sample):

public class ReportingService 
{
[OperationContract]
public string[] GetReportList()
{
var path = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "Resources");
return Directory.GetFiles(path, "*.pdf");
}

[OperationContract]
public byte[] GetReportStream(string reportName)
{
// get file name
var path = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "Resources");
reportName = Path.Combine(path, reportName);

// load file into stream
var ms = new MemoryStream();
var buff = new byte[64000];
using (var sr = new FileStream(reportName, FileMode.Open))
{
for (; ; )
{
int read = sr.Read(buff, 0, buff.Length);
ms.Write(buff, 0, read);
if (read == 0)
{
break;
}
}
}

// return stream
return ms.ToArray();
}

}


That's it for the web service.  To make sure everything is working, set ReportingService.svc as the startup page, and hit F5.  A service information should display in your default browser.  Copy the URL from the address bar, we need it for our Silverlight project.



image



Building the Silverlight Client



In order for our Silverlight client to know what operations are available, we need to configure a Service Reference.  To add a service reference, stop the debugging, but leave the development web server running.  Right-click on the Silverlight app (ReportViewerQuickStart), and choose Add Service Reference.  Paste in the URL of the service-you should see the service listed.  Expand the service and make sure the two operations are listed.  It's a good idea to change the Namespace of the service to something more useful.  Click OK, and the classes we need to consume the service will be generated.



image



Our viewer will be simple-just a combobox to select the report, and a viewer control to display the report.  Our MainPage.xaml layout looks like this:



image



It's time to wire up a report list.  In MainPage.xaml.cs, we need to add another using statement for System.IO, and call GetReportList().  Our code looks like below (the full file is in the sample):



public partial class MainPage : UserControl 
{
public MainPage()
{
InitializeComponent();

// go get the list of reports available
var svc = new ReportingServiceReference.ReportingServiceClient();
svc.GetReportListCompleted = svc_GetReportListCompleted;
svc.GetReportListAsync();

}

// populate ComboBox with list of reports available on the server
void svc_GetReportListCompleted(object sender, ReportingServiceReference.GetReportListCompletedEventArgs e)
{
_cmbReport.Items.Clear();
foreach (string file in e.Result)
{
_cmbReport.Items.Add(Path.GetFileName(file));
}
if (_cmbReport.Items.Count > 0)
{
_cmbReport.IsEnabled = true;
_cmbReport.SelectedIndex = 0;
}
}
}


To check our progress, set ReportViewerQuickStartTestPage.aspx as the start page, and hit F5.  We should see our reports listed in the combobox.



image



Time to display the report!  To the combobox, add a SelectionChanged attribute; the XAML should look like below:





Add a C1ReportViewer control to the grid on MainPage.xaml, and in MainPage.xaml.cs, retrieve the selected report and bind it to the report viewer:



// show the report that was selected 
void ReportType_Click(object sender, EventArgs e)
{
// build report name
string reportName = (string)_cmbReport.SelectedItem;

// go get the stream
var svc = new ReportingServiceReference.ReportingServiceClient();
svc.GetReportStreamCompleted = svc_GetReportStreamCompleted;
svc.GetReportStreamAsync(reportName);
}

// display the report
void svc_GetReportStreamCompleted(object sender, ReportingServiceReference.GetReportStreamCompletedEventArgs e)
{
var ms = new MemoryStream(e.Result);
_reportViewer.LoadDocument(ms);
}


That's it!  Run the sample again, and the report viewer should display the first report in the list.  Selecting a different report will change what is displayed in the viewer.



The sample project takes this demo one step further, and allows users to choose between PDF or MHTML formats.