Caffeine-Powered Life

Simple Report Generation in ASP.NET MVC

Disclaimer: The examples shown here are written using Crystal Reports Beta 2 in VS 2010. You can download the Crystal Report designer from Microsoft. At the time of this writing, the developer bits for Crystal Reports can be downloaded from here. You’ll also need to get the .NET 4 runtime in either 32-bit or 64-bit, as necessary for your server configuration.

Reporting is a basic necessity in many applications. In ASP.NET WebForms, this usually isn’t a big deal. Build your report, drop a Report Viewer control onto the screen and go about your merry way. Without the more sophisticated “control” world in MVC, many developers get confused as how to do some fairly simple tasks. Yes the controls are convenient, but they aren’t that difficult to recreate.

First, let’s define our (very simple) report. I want a list of customers and YTD sales. Let’s suppose we have an object model for this. In my example, it’s a database table, but you can build your report from any class.

Next, I’ll create a folder in my web project called ReportTemplates. Any guess what we’re going to put here?

I’ve created a report named “SalesYTD.rpt”. (I just created a blank report.) The first thing I need to do is select a data source. Crystal can connect to DataSets, Access databases, ODBC data sources, and dozens of other things. This time, though, we’re going to connect to an object. Select Project Data, .NET Objects, then select your Customer class.

As I write my report, I can now drag my two fields, Name and SalesYTD onto my report.

Now it’s time to create my controller.


public class ReportsController : Controller

{

  private readonly ReportSampleDataContext _db = new ReportSampleDataContext();



  public ActionResult SalesYtd()

  {

    var reportPath = Server.MapPath("~/ReportTemplates/SalesYTD.rpt");

    var customers = _db.Customers;



    using (var reportDocument = new ReportDocument())

    {

      reportDocument.Load(reportPath);

      reportDocument.SetDataSource(customers);



      var response = System.Web.HttpContext.Current.Response;

      reportDocument.ExportToHttpResponse(ExportFormatType.PortableDocFormat, response, true, "SalesYTD");

    }

    return new EmptyResult();

  }

}  

This will download your report as an attachment. Voila!

A few things to note here…

  1. Please, please, please. Make sure that you wrap the report document in a using statement. Report documents are huge and very memory-leaky.
  2. You have to use the System.Web.HttpContext.Current.Reponse. You can’t use the Response object that’s part of the controller, since that’s really an instance System.Web.HttpResponseWrapper.
  3. The return action is actually an EmptyResult. The report document is written directly to the HTTP response stream.

Yes, that’s really it. Your report is done. I’ll create a home/index controller and page. I’ll add a link like this…


<%: Html.ActionLink("Print My Report", "SalesYTD", "Reports") %>

In my next post, we’ll talk about DRY-ing up the code and making this testable.

Updates

Comments