Introduction
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.
Background
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:
<!---->
<system.net>
<mailSettings>
<smtp deliveryMethod="Network" from="no-reply@mywebsite.com">
<network host="localhost" port="25" defaultCredentials="true"/>
</smtp>
</mailSettings>
</system.net>
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:
System.Net.Mail
Microsoft.Reporting.WebForms
System.IO
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 =
new MyFirstDataSetTableAdapters.MyFirstTableAdapter();
MySecondDataSetTableAdapters.MySecondTableAdapter ta2 =
new MySecondDataSetTableAdapters.MySecondTableAdapter();
ReportDataSource ds1 = new ReportDataSource
("MyFirstDataSet_MyFirstTableAdapter", ta1.GetData(myParam1, myParam2));
ReportDataSource ds2 = new ReportDataSource
("MySecondDataSet_MySecondTableAdapter", ta2.GetData(myParam3));
You can see in the code above that we create our table adapters, ta1
and 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 GetData
method.
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
.
lr.DataSources.Add(ds1);
lr.DataSources.Add(ds2);
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.
Warning[] warnings;
string[] streamids;
string mimeType;
string encoding;
string extension;
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);
s.Seek(0, SeekOrigin.Begin);
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(
"my_friend@herEmail.com", "me@myEmail.com",
"A report for you!", "Here is a report for you");
message.Attachments.Add(a);
SmtpClient client = new SmtpClient();
client.Send(message);
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 SmtpClient
and 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.
History
- Sept. 20, 2007 - First Revision