Returning a Crystal report as a PDF ActionResult in ASP.Net MVC

Photo by Edz Norton on Unsplash

Returning a Crystal report as a PDF ActionResult in ASP.Net MVC

In a recent project I needed to display a generated PDF contract document inside the browser. I found a great article by Patrick Kalkman illustrating how to create a custom ActionResult that converts a HTML string into a PDF using the iTextSharp library.

This seemed like the perfect solution, however, I later found that the conversion from HTML to PDF is not perfect at all and getting your design to look the same in PDF as it does in HTML is not easy. So I found another article by Hasibul Haque, where he shows how to return a Crystal Report as PDF using ASP.Net MVC.

The Crystal Report approach works well, as your design will look perfect. The only problem I had was Hasibul’s approach returned the PDF as a download and I wanted to display it inside the browser. I combined Patrick and Hasibul’s approaches and what I came up with was a CrystalReportPdfResult

The CrystalReportPdfResult is an ASP.Net ActionResult, that return the Crystal Report as a PDF file. The result (using an iframe) looks like this inside the browser:

image.png

Creating the CrystalReportPdfResult class

To generate the result as indicated by the previous image, follow these steps:

  1. Create a folder inside your ASP.Net MVC project called Pdf.
  2. Add a new class to this folder called CrystalReportPdfResult.cs.
  3. The new class will inherit from the ASP.Net MVC ActionResult class.

The entire code listing for the CrystalReportPdfResult class follows:

CrystalReportPdfResult.cs

public class CrystalReportPdfResult : ActionResult
{
    private readonly byte[] _contentBytes;

    public CrystalReportPdfResult(string reportPath, object dataSet)
    {
        ReportDocument reportDocument = new ReportDocument();
        reportDocument.Load(reportPath);
        reportDocument.SetDataSource(dataSet);
         _contentBytes = StreamToBytes(reportDocument.ExportToStream(ExportFormatType.PortableDocFormat));
     }

     public override void ExecuteResult(ControllerContext context)
     {

         var response = context.HttpContext.ApplicationInstance.Response;
         response.Clear();
         response.Buffer = false;
         response.ClearContent();
         response.ClearHeaders();
         response.Cache.SetCacheability(HttpCacheability.Public);
         response.ContentType = "application/pdf";

         using (var stream = new MemoryStream(_contentBytes))
         {
             stream.WriteTo(response.OutputStream);
             stream.Flush();
         }
     }

     private static byte[] StreamToBytes(Stream input)
     {
         byte[] buffer = new byte[16 * 1024];
         using (MemoryStream ms = new MemoryStream())
         {
             int read;
             while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
             {
                 ms.Write(buffer, 0, read);
             }
             return ms.ToArray();
         }
     }
 }

Creating the Crystal Report

Next, we need to create the Crystal Report that will be returned as a PDF. Do this by following these steps:

  1. Create a new folder called Reports in your project.
  2. Add a new Crystal Report object to the Reports folder. You can find the item template under the Reporting tab on the Add New Item dialog. image.png
  3. Design the report to your liking and save it.
  4. If you need to install Crystal Reports for Visual Studio 2013, get it here.

Creating the Action that will return the PDF

Next, we need to create the actual Action on a Controller that will return the CrystalReportPdfResult, by following these steps:

  1. Open your controller. In this example, we’ll use the HomeController.
  2. Add a new method called Pdf that will return a CrystalReportPdfResult object.
  3. Inside this method, we’ll build a list of customer that will be used as data for the report and build the path to the report filename. The code for the method follows:
public CrystalReportPdfResult Pdf()
{
    List<Customer> model = new List<Customer>();
    model.Add(new Customer { CompanyName = "Blah Inc.", ContactName = "Joe Blogs" });
    string reportPath = Path.Combine(Server.MapPath("~/Reports"), "rptCustomers.rpt");
    return new CrystalReportPdfResult(reportPath, model);
}

Creating the View

We’ll embed the PDF using an , so open a view. In this case we’ll use About.cshtml and replace it’s mark-up with the following:

About.cshtml

@{
    ViewBag.Title = "Customer Report";
}
<h2>@ViewBag.Title.</h2>
<p>This is the customer report</p>

<div class="row">
    <div class="col-md-10">
         <iframe src="@Url.Action("Pdf", "Home")" height="500" width="100%"></iframe>
     </div>
     <div class="col-md-2">
         <h3>Other page content</h3>
         <button type="button" class="btn btn-primary">Click here</button>
     </div>
 </div>

Note, that we’re using the @Url.Action helper to set the src attribute of the

In case you’re wondering how to send the Crystal Report as a PDF attachment on an email, here is the code:

Code Snippet

public void SendReport()
        {
            List<Customer> model = new List<Customer>();
            model.Add(new Customer { CompanyName = "Blah Inc.", ContactName = "Joe Blogs" });

            ReportDocument reportDocument = new ReportDocument();
            reportDocument.Load(Path.Combine(Server.MapPath("~/Reports"), "rptCustomers.rpt"));
            reportDocument.SetDataSource(model);
            using (var stream = reportDocument.ExportToStream(ExportFormatType.PortableDocFormat))
             {
                 SmtpClient smtp = new SmtpClient
                 {
                     Port = 587,
                     UseDefaultCredentials = true,
                     Host = "smtp.gmail.com",
                     EnableSsl = true
                 };

                 smtp.UseDefaultCredentials = false;
                 smtp.Credentials = new NetworkCredential("you@gmail.com", "gmailPassword");

                 var message = new System.Net.Mail.MailMessage("you@gmail.com", "recipient@gmail.com", "subject", "body");
                 message.Attachments.Add(new Attachment(stream, "report.pdf"));

                 smtp.Send(message);
             }
         }

I’ve tested this on the latest version of Chrome, Firefox and Internet Explorer and it works on all of them.

Hope this helps someone that needs something similar. Thank you for reading. Until next time, keep coding!

Download the sample project here.

If you have any comments or questions, please feel free to ask leave a comment or drop me a line on Twitter.