Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Email a LocalReport as a PDF Attachment

0.00/5 (No votes)
20 Sep 2007 1  
Using a LocalReport and ReportDataSources, stream a PDF as an attachment to an email

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:

<!-- SMTP Settings -->
    <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

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here