Frequently, I'll have add-on reports that we don't necessarily want to deploy to a full Reporting Services installation. I don't need the scheduling or other features of Reporting Services and would rather just run them in-application and return the result. To this end, I've written a "WebReportRender" class that takes an .rdlc and renders it as a PDF to the browser. In WebForms, this meant some goo to render the Http headers to trigger a file download in the browser. I've wrapped all that nicely and it's served me well the past few years. Now, ASP.NET MVC adds a nice FileContentResult which wraps some of this goo for you. To reuse WebReportRender in ASP.NET MVC, I had to add methods to just output the byte array without the Http Headers:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Mail;
using System.Text;
using System.Web;
using Microsoft.Reporting.WebForms;
namespace LifeCycle.Reporting
{
/// <summary>
/// Assists in executing and rendering Reporting Service reports.
/// </summary>
public class WebReportRenderer : IDisposable
{
#region Private fields
private string fullReportPath;
private string downloadFileName;
private LocalReport reportInstance;
private MemoryStream reportMemoryStream;
private string reportMimeType;
#endregion
#region Constructor
/// <summary>
/// Initializes a new instance of the <see cref="WebReportRenderer"/> class.
/// </summary>
/// <param name="reportPath">The report path.</param>
/// <param name="downloadFileName">Name of the download file.</param>
public WebReportRenderer(string reportPath, string downloadFileName)
{
if (HttpContext.Current == null) throw new InvalidOperationException("This class is only for use from web applications.");
this.downloadFileName = downloadFileName;
fullReportPath = HttpContext.Current.Server.MapPath(reportPath);
using (System.IO.FileStream reportFile = new System.IO.FileStream(fullReportPath, System.IO.FileMode.Open, FileAccess.Read))
{
reportInstance = new LocalReport();
reportInstance.LoadReportDefinition(reportFile);
}
}
#endregion
#region Properties
/// <summary>
/// Gets the report instance.
/// </summary>
/// <value>The report instance.</value>
public LocalReport ReportInstance
{
get
{
return reportInstance;
}
}
#endregion
#region Public Methods
/// <summary>
/// Renders the current ReportInstance to the user's browser as a PDF download.
/// </summary>
/// <param name="pdfDeviceInfoSettings">The PDF device info settings (see http://msdn2.microsoft.com/en-us/library/ms154682.aspx).</param>
/// <returns></returns>
public Warning[] RenderToBrowserPDF(string pdfDeviceInfoSettings)
{
CreateStreamCallback callback = new Microsoft.Reporting.WebForms.CreateStreamCallback(CreateWebBrowserStream);
Warning[] warnings;
HttpContext.Current.Response.ContentType = "application/octet-stream";
HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment; filename=\"" + downloadFileName + "\"");
reportInstance.Render("PDF", null, callback, out warnings);
return warnings;
}
/// <summary>
/// Renders the current ReportInstance to the user's browser as a PDF download.
/// </summary>
/// <returns></returns>
public Warning[] RenderToBrowserPDF()
{
return RenderToBrowserPDF(null);
}
/// <summary>
/// Renders the current ReportInstance to an email with a file attachment containing the report.
/// </summary>
/// <param name="pdfDeviceInfoSettings">The PDF device info settings (see http://msdn2.microsoft.com/en-us/library/ms154682.aspx).</param>
/// <param name="toAddress">To address.</param>
/// <param name="fromAddress">From address.</param>
/// <param name="subject">The subject.</param>
/// <param name="body">The body.</param>
/// <returns></returns>
public Warning[] RenderToEmailPDF(string pdfDeviceInfoSettings, string toAddress, string fromAddress, string subject, string body)
{
CreateStreamCallback callback = CreateMemoryStream;
Warning[] warnings;
reportInstance.Render("PDF", pdfDeviceInfoSettings, callback, out warnings);
reportMemoryStream.Seek(0, SeekOrigin.Begin);
var client = new SmtpClient();
using (var message = new MailMessage(fromAddress, toAddress, subject, body))
using (var attachment = new Attachment(reportMemoryStream, reportMimeType))
{
attachment.Name = downloadFileName;
message.Attachments.Add(attachment);
client.Send(message);
}
return warnings;
}
public byte[] RenderToBytesPDF()
{
string mimeType;
string encoding;
string fileNameExtension;
string[] streams;
Warning[] warnings;
return reportInstance.Render("PDF", null, out mimeType, out encoding, out fileNameExtension, out streams,
out warnings);
}
public byte[] RenderToBytesExcel()
{
string mimeType;
string encoding;
string fileNameExtension;
string[] streams;
Warning[] warnings;
return reportInstance.Render("EXCEL", null, out mimeType, out encoding, out fileNameExtension, out streams,
out warnings);
}
/// <summary>
/// Renders the current ReportInstance to an email with a file attachment containing the report.
/// </summary>
/// <param name="toAddress">To address.</param>
/// <param name="fromAddress">From address.</param>
/// <param name="subject">The subject.</param>
/// <param name="body">The body.</param>
/// <returns></returns>
public Warning[] RenderToEmailPDF(string toAddress, string fromAddress, string subject, string body)
{
return RenderToEmailPDF(null, toAddress, fromAddress, subject, body);
}
#endregion
#region Private Methods
/// <summary>
/// Formats the current response output and returns the output stream suitable for rendering to the browser as a file download.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="extension">The extension.</param>
/// <param name="encoding">The encoding.</param>
/// <param name="mimeType">Type of the MIME.</param>
/// <param name="willSeek">if set to <c>true</c> [will seek].</param>
/// <returns></returns>
private Stream CreateWebBrowserStream(string name, string extension, System.Text.Encoding encoding, string mimeType, bool willSeek)
{
return HttpContext.Current.Response.OutputStream;
}
/// <summary>
/// Creates a memory stream that can be used by the report when rendering to an email attachment.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="extension">The extension.</param>
/// <param name="encoding">The encoding.</param>
/// <param name="mimeType">Type of the MIME.</param>
/// <param name="willSeek">if set to <c>true</c> [will seek].</param>
/// <returns></returns>
private Stream CreateMemoryStream(string name, string extension, System.Text.Encoding encoding, string mimeType, bool willSeek)
{
reportMemoryStream = new MemoryStream();
reportMimeType = mimeType;
return reportMemoryStream;
}
#endregion
#region IDisposable Members
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
if (this.reportInstance != null) this.reportInstance.Dispose();
if (this.reportMemoryStream != null) this.reportMemoryStream.Dispose();
}
#endregion
}
}
Using WebReportRenderer from ASP.NET MVC is simple:
byte[] result;
using (var renderer = new WebReportRenderer(@"~\Report.rdlc", "Report.pdf"))
{
var adapter = new ReportTableAdapter();
renderer.ReportInstance.DataSources.Add(new ReportDataSource("ReportDataSet_Report", adapter.GetData()));
ReportParameter p0 = new ReportParameter("p0", someValue);
renderer.ReportInstance.SetParameters(new[] {p0});
result = renderer.RenderToBytesPDF();
}
return File(result,"application/pdf", "Report.pdf");
This isn't something you'd use for high volume reports, or if you need advanced delivery or scheduling capabilities. But for one-off reports that only a few people will ever use, it works pretty well.
0 comments:
Post a Comment