SSRS

Emailing an SSRS Report using ReportViewer

Posted on May 26, 2009. Filed under: SSRS | Tags: , , , , |

Introduction

SQL Server Reporting Services (SSRS) is Microsoft’s offering in the reporting space, if you install SSRS as part of a SQL Server install you get a nice web front end through which users can navigate and view reports. But SSRS is not restricted to this online portal style access, reports can be embedded into you application using the ReportViewer component shipped with Visual Studio. 

 Through the ReportViewer component users can view, export and print reports in exactly the same manner as the web based version but we also get some nice hooks into the ReportViewer control, I’m going to demo how we can use these to extract the report to a pdf and attach it to a new outlook message ready for the user to view and send. I’m using the inbuilt Outlook interoperability to achieve this but you could just as easily apply the same principles to another method of email delivery. 

 I’m doing this demo using VS2008 but it will work in exactly the same manner in VS2005. 

Adding a ReportViewer 

For this demo I’m starting with a blank form, first we need to drag a ReportViewer control onto the design surface. I’m using the a report stored on an SSRS server in this demo but you can also include reports locally in your solution, whichever option you choose does not affect the following steps. 

 

Configure the report viewer to point to your report, I’m using a server based report so I have expanded the “ServerReport” property and specified a report server and a report path, I have also changed the “ProcessingMode” property to “Remote” to specify that I am using a remote report. 

 

Now when we run the application we are able to interact with the report. 

Exporting the Report 

To attach our report to an email the first step is to get the pdf file out to disk, I’m going to write a utility type function to achieve this which will return a filepath to the saved PDF file; 

//You will need to import these namespaces
using Microsoft.Reporting.WinForms;
using System.IO;

 

/// <summary>
/// Exports the current report out to a pdf file
/// </summary>
/// <returns>
/// Path to the file that was generated
/// </returns>
private string ExportReport()
{
    Warning[] warnings;
    string[] streamids;
    string mimeType;
    string encoding;
    string filenameExtension;
 
    //Render the report to a byte array
    byte[] bytes;
    if (reportViewer1.ProcessingMode == ProcessingMode.Local)
    {
       bytes = reportViewer1.LocalReport.Render("PDF", null, out mimeType,
           out encoding, out filenameExtension, out streamids, out warnings);
    }
    else
    {
       bytes = reportViewer1.ServerReport.Render("PDF", null, out mimeType,
          out encoding, out filenameExtension, out streamids, out warnings);
    }

    //Write report out to temporary PDF file
    string filename = Path.Combine(Path.GetTempPath(), "ReportToAttach.pdf");
    using (FileStream fs = new FileStream(filename, FileMode.Create))
    {
        fs.Write(bytes, 0, bytes.Length);
    }

    //return path to saved file
    return filename;
}

Sending the Email 

The last step is to attach the report to a new email and load it up for the user, I’m using the Outlook interoperability assemblies. If you want to use the same method you will need to add a reference to Microsoft.Office.Interop.Outlook to your project. 

Let’s add a button to our form to kick of the process and in the event handler we will create an email and attach our report. 

//You will need to import this namespaces
using Microsoft.Office.Interop.Outlook;

 

private void emailButton_Click(object sender, EventArgs e)
{
    //Write report out to temporary PDF file
    string reportFilename = ExportReport();

    // Create a new email using outlook
    ApplicationClass outlookApp = new ApplicationClass();
    MailItem mailItem = (MailItem)outlookApp.CreateItem(OlItemType.olMailItem);
    mailItem.To = "mail@yourdomain.com";
    mailItem.Subject = "Your Report";
    mailItem.Body = "Please find your report attached";
    mailItem.Attachments.Add(reportFilename, (int)OlAttachmentType.olByValue, 1, reportFilename); 

    //Remove the temp file once attached
    File.Delete(reportFilename);
 
    // Display the window
    mailItem.Display(false);
}

Now when we click the button we should get a new outlook message with the PDF attached; 

Read Full Post | Make a Comment ( 6 so far )

Searchable / Dependant Parameters in SSRS

Posted on July 29, 2008. Filed under: SSRS | Tags: , , |

Introduction

The idea behind this post came from a question from a colleague, they wanted an SSRS report where they could select from a list of employees but the list could be filtered by searching on the surname (i.e. you enter ‘S’ and it would load a dropdown of all employees whose surname start with S).

It’s quiet an interesting requirement because we have very little control over the manner in which parameters are selected by the user. There is however a very simple way to achieve this utilising dependant parameters, in SSRS we can set the default value or available values to be dependant on the value selected in a previous parameter.

Getting Data On The Report

Lets start from the data for the report and work backwards, I’m using the Adventure Works sample database and we are going to load pay rate records for an employee.

We can create a new dataset that selects all pay records for an employee;

I’ll add a simple table to the report to display the returned records;

And when we run the report we are prompted to enter an employee ID;

Adding a Searchable Drop Down for Employee

SSRS will allow us to use a query to retrieve possible values for a parameters, so lets add a dataset that will load a list of employees whose surname starts with a specified search phrase;
Note the @Search parameter that is highlighted

Now if we select Report -> Report Parameters from the menu we will see Visual Studio has automatically generated an EmployeeID and Search parameter for us. First we need to move the Search parameter to be the first in the list since the values of the EmployeeID are dependant on it. Next highlight the EmployeeID parameter and in the “Available Values” sections tick the “From Query” radio button, select CandidateEmployees as the dataset, EmployeeID as the value field and DisplayName as the Label Field.

When we run the report the Employee dropdown is greyed out, if we enter “R” into the search box and then press tab the employee dropdown will be populated with all employees whose surname starts with R, we can select one and then click “View Report”… Hey Presto!!!

Read Full Post | Make a Comment ( 2 so far )

SSRS Custom Code (Expressions, Embedded Code & External Assemblies)

Posted on July 9, 2008. Filed under: SSRS | Tags: , , , |

Introduction

In this article we will look at the options for using custom .Net code in our Microsoft SQL Server Reporting Services (SSRS) reports. This post assumes you have a working knowledge of SSRS.

There are three main ways to use code to help generate our reports, in increasing order of complexity (and therefore flexibility) these are;

  • Expressions
  • Embedded Code
  • External Assemblies

The Sample Solution

To demonstrate the different options we will work with a simple report example based on the Adventure Works database
The Adventure Works database can be downloaded at http://www.codeplex.com/MSFTDBProdSamples/Release/ProjectReleases.aspx?ReleaseId=4004

I’ve setup one dataset which returns the total number and revenue of sales for each territory;

 

I’ve placed a single table on the report that displays each row from our dataset;

 

 When the report runs it looks something like this;

 

Expressions

Expressions in SSRS are a very similar concept to formulas in Excel, we are going to add an extra column to our report to display the Average Sale Revenue for each territory. In the detail cell we can type the following formula to display the result of dividing the revenue by the number of sales;

=Fields!SalesAmount.Value/Fields!SalesCount.Value

Note that if you right click on the cell you will get an ‘Expression…’ option in the context menu which will give you a nice little GUI for designing your expression. Now if we run the report it should look something like the following;

 

Expressions aren’t limited to cells in tables so if we add the following textbox to the page header of our report it will display the name of the report at the top of the report, this is really handy because if we change the name of a report we don’t need to worry about adjusting the header as well.

 

You will also notice that you can use expressions to adjust properties of report controls, for example the background colour of our table can be set using a property, in fact we can adjust any properties apart from the size and position of controls using expressions.

Embedded Code

Now obviously expressions have their limitations, they aren’t reusable throughout our report as we need to copy and paste them into each cell or property and they can’t really execute any logic.

What we can do is write report wide functions using .Net code and then call these functions from expressions. For example let’s add some colouring to our table based on the average sale, territories with an average sale of under $5,000 will be shaded red, under $10,000 will be shaded orange and $10,000+ will be shaded green.

We could achieve this using expressions (utilising nested IIF statements) but if we wanted to change one of the thresholds we would have to change it on every cell that was shaded. A much nicer solution would be to create a function that accepts an average sale amount and returns the appropriate colour.

To open the embedded code section select ‘Report->Report Properties…’ from the menu and open the ‘Code’ tab, below is the function I have created to calculate our background colour;

 

If we select the detail row of our table and enter the following expression into the BackgroundColor attribute;

=Code.GetColour(Fields!SalesAmount.Value/Fields!SalesCount.Value)

 

Now when we run the report we should get something like;

 

External Assemblies

Embedded code also has its limitations, we can’t reuse our functions between reports without copy/pasting and we don’t get intelli-sense and other niceties that we have all grown to depend on. To re-use functions between reports we need to create a class library and then reference the resulting compiled assembly from our report.

I’ve added a C# Class Library project to my solution and added a single ‘ReportUtils’ class as follows;

 

The code for ReportUtils.cs is as follows;

using System;
using System.Collections.Generic;
using System.Text;

namespace RowansCustomLibrary
{
    public sealed class ReportUtils
    {
       public static string ReportFooter()
        {
            return "Printed " +
                DateTime.Now.ToString("dd-MMM-yyyy HH:mm") +
                " by " + Environment.UserName;
        }
    }
}

Now to make our assembly easily available other .Net code I am going to load it into the Global Assembly Cache (GAC), to do this I need to “Strong Name” the assembly. Basically this involves versioning and signing the dll that is produced during build, to do this right click on the class library project and select properties, open the Signing tab and tick the Sign the assembly box, you can use an existing key if you have one or Visual Studio will create one for you if you select New….


Now build your class library (Ctrl+Shift+B), preferably in “Release” mode. To install your new assembly into the GAC just open up the bin\release folder of your project and drag the dll into the C:\WINDOW\assembly directory on your machine (you can also use the command line “gacutil” tool to install assemblies into the GAC).

To reference our new assembly from the SSRS project we need to;

  1. Select ‘Report->Report Properties…’ from the menu
  2. 2. Select the ‘References’ tab 
  3. Click the ‘..’ button
  4. Select the ‘Browse’ tab
  5. Navigate to the dll in the bin\release folder of the class library project
  6. Click the ‘Add’ Button
  7. Click ‘OK’ and then ‘OK’

Note: Although we have selected the dll from our project file it will actually load the GAC version when the report runs.

Now we can add a textbox to the footer of our report that uses our assembly;

 

 

We will also need to deploy our assembly to the GAC on any machines where our report is executed, in most cases this would be your report server.

Read Full Post | Make a Comment ( 3 so far )

Horizontally Centred Dynamic Images in SSRS

Posted on May 9, 2008. Filed under: SSRS | Tags: , , |

Centring Dynamic Logos in SSRS

This was an interesting problem I ran into the other day, basically I have a report that displays data on a range of businesses. The report then needed a dynamic header to display the logos for each of the businesses that feature in the report. The number of businesses could vary anywhere from 1 to 11 depending on the parameters selected.

In my case I was using images stored along side the reports but you could achieve exactly the same thing using images from a database.

Getting the images on the report

The image to display was derived from one of the other columns in my dataset so I added a derived field called ImageURI;



As tables are only capable of grouping in a vertical direction and I wanted to display the logos across the top of the page I opted for a Matrix. After adding a Matrix to the report and setting the appropriate dataset the next step was to add a column grouping for our ImageURI field;



I then dragged an image control into the header cell of the column (click finish to get out of the wizard) and then in the properties pane set the source to be external and the value to be the ImageURI field;



Now when we run the report we get all the appropriate logos displayed across the top of the page, the next step is to evenly space them to fill the entire page width.



Centring the Images

Now the options for modifying the layout of a Matrix are reasonably limited, primarily because we have no idea how many columns there are going to until run time and cells will automatically re-size to fit whatever data is thrown at them.

Based on this I used the “Padding” attribute of the image to force each cell to grow enough to fill the width of the page. I targeted the left padding of each image which will result in adding a bunch of white space to the left of each cell and pushing the image to the right hand side.
This is the original equation I used;

=(460 / (CountDistinct(Fields!ImageURI.Value, "Products")) - 80).ToString & "pt"
Note: 460 is the total width of my page and 80 is the width of each image



Now all I needed to do was get rid of the padding on the first image, to do this I modified my expression to only apply the padding if this was not the first grouping in the matrix, I achieved this using the RunningValue function as follows;

=iif(RunningValue(1, Sum, "matrix2 ") > 1,((380 / (CountDistinct(Fields!ImageURI.Value, "Products") - 1)) - 80).ToString & "pt", "0pt")

The result is nicely centred images as required. Obviously this approach only works if you have fixed width images (80px in my case), if not you will need to take the width of each image into account in the padding formula, a project for another day.



Read Full Post | Make a Comment ( None so far )

    About

    Rowan works as a Program Manager for the ADO.NET Entity Framework team at Microsoft. He speaks at technical conferences and blogs at romiller.com. Rowan lives in Seattle, Washington with his wife Athalie. Prior to moving to the US he resided in the small state of Tasmania in Australia. Outside of technology Rowan's passions include snowboarding, mountain biking, horse riding, rock climbing and pretty much anything else that involves being active. The primary focus of his life, however, is to follow Jesus.

    RSS

    Subscribe Via RSS

    • Subscribe with Bloglines
    • Add your feed to Newsburst from CNET News.com
    • Subscribe in Google Reader
    • Add to My Yahoo!
    • Subscribe in NewsGator Online
    • The latest comments to all posts in RSS

    Meta

Liked it here?
Why not try sites on the blogroll...

Follow

Get every new post delivered to your Inbox.

Join 86 other followers