If you are using the ReportViewer or ReportingServices in your application and have ever said to yourself, "You know, it would be really nice if I could take this report and attach it as an email and send it to someone", then you are in the right spot. In my last project, we were using the ReportViewer and had many reports lying around and I found myself saying just that. I didn't want to write the PDF's to the file system of the server, then attach it to the email and then clean up after myself. I wanted to stream it directly into the email. Here is how I accomplished this task.
This article assumes that you have your web site set up to use the standard SMTP settings for ASP.NET 2.0. Your web.config should have a section that looks something like this:
<smtp deliveryMethod="Network" from="firstname.lastname@example.org">
<network host="localhost" port="25" defaultCredentials="true"/>
This article will assume that you also have a working report and it's associated datasource(s). It's beyond the scope of this article to explain creating the report or using the report viewer. There are plenty of tutorials just on doing that. This will focus on creating the report in memory and streaming it into an email as an attachment.
Using the Code
Make sure that you have all the namespaces that you are going to need in your code. We will be using the following:
So in order to begin setting up our report as an attachment, the first thing that you must do is, get the report and it's data sources set up correctly.
LocalReport lr = new LocalReport();
lr.ReportPath = HttpContext.Current.Server.MapPath("~/Reports/myReport.rdlc");
The code above creates a new
LocalReport in memory and sets the path to the actual report definition local client (.rdlc) file. The next step in the process is to get the datasources for the report setup. We do this by using table adapters and adding them to the local reports datasource collection. For this example, we will say that we have two data sources in our report, perhaps there is a sub-report that is displayed.
MyFirstDataSetTableAdapters.MyFirstTableAdapter ta1 =
MySecondDataSetTableAdapters.MySecondTableAdapter ta2 =
ReportDataSource ds1 = new ReportDataSource
("MyFirstDataSet_MyFirstTableAdapter", ta1.GetData(myParam1, myParam2));
ReportDataSource ds2 = new ReportDataSource
You can see in the code above that we create our table adapters,
ta2. We then create two instances of a
ReportDataSource using our table adapters'
GetData method as the
dataSourceValue. You might have to pass a couple of parameters to the table adapter method as we did here - it all depends on your architecture. Here we just showed what it would look like by passing a couple of pseudo parameters to our
The critical part here is making sure that the name of the datasource is EXACTLY the same as it is in the report. You can get this information by looking at the datasources in the report designer and basically copying and pasting the values. If you get the names incorrect, the report will not generate at all.
We then add each
ReportDataSource to the
LocalReport datasources collection by calling the
Add method and passing in the appropriate variables
So from this point, we should have a
LocalReport, with two datasources that are mapped to a couple of tables from our datasets, all set and ready to go. The next step will be to set up the email attachment. To do this, we will need to render the
LocalReport into a stream that we can use for the email attachment. Step one is to set up all the variables that the
Render method of the
LocalReport needs and then render the report into memory.
byte bytes = lr.Render("PDF", null, out mimeType,
out encoding, out extension, out streamids, out warnings);
You can see here that we set the format that we want out of the report to PDF. We could also use Excel or another valid type of output here if we wanted to. We also sent in
null as the value for
deviceInfo. If you wanted to, you could send in a string with the properly formatted XML device information. The rest of the parameters are output parameters as you can see. For this application, we don't use any of them. Once we execute this code, we now have a
localReport, stored as a string of bytes in memory. We are now ready to stream these bytes into our email as an attachment.
MemoryStream s = new MemoryStream(bytes);
Here we create a
MemoryStream from our string of bytes and set the position to the beginning of the stream by seeking there. If we did not seek the beginning of the stream, when we go to create the attachment, we would be at the end of the stream and nothing would get added in our attachment.
Attachment a = new Attachment(s, "myReport.pdf");
We create a new email
Attachment and pass in our
MemoryStream (which was the string of bytes we got from executing the
Render method of our
LocalReport) and a name for the attachment. We are now ready to create an email, add our attachment and send the report on it's way to our lucky recipient.
MailMessage message = new MailMessage(
"A report for you!", "Here is a report for you");
SmtpClient client = new SmtpClient();
First we create the mail message and add in the from, to, subject and body string parameters. We then add the attachment to the messages attachments collection by executing the
Add method and passing our memory stream as the parameter. We then get a new
Send the message on it's way.
Points of Interest
The nice thing about creating the report as a stream and attaching it to the email this way is that it is all done in memory. There's no file creation and cleanup work that has to be done. Any report you create for use in the ReportViewer can be used here as well.
- Sept. 20, 2007 - First Revision