Step by Step adding an RDLC report to MVC4 project
For any large project, reports will be needed. I love RDLC
so I’m sticking with them. I had some problems in the beginning but with some
search here and there and some testing it start working. So I will describe it
from the beginning.
Requirements:
·
In my new MVC I need to
have a number of RDLC reports
·
I need the reports to read
from objects, direct entities or report objects (for large reports I prefer to something
like a Report View Model, in the end a report is view for data)
·
A report my require one or
more data sets
·
Some of the reports will
need parameters that the user will select from the MVC pages
·
I want to export the report
by PDF, but having word and Excel would
be an advantage
·
Some report standardization
point that is needed:
o
Required reports have a description
title
o
Required have right and
left main and sub headers
o
Required to have the user name
printing the report in the footer
o
Required to have the
printing date on the footer
o
Required to have number of
page and total pages in the footer
o
All the reports are A4
stander reports ready for printing.
o
Reports will be multi language
(currently English and Arabic)
o
Reports may contain charts
and bars
o
Reports need to be secure
·
The system contains an
alert module and some of the requirement needs the PDF document to be attached
to the email.
So lets start clean and from the beginning.
Lunching Visual Studio, selecting new project
Selecting MVC4, intrenet application (all template will work
too)
Adding a new project with the name: ASPNET_MVC_RDLC
I will start putting it all in one project; it’s the same if
you have business and data access layers that are in separate projects too, only to make it short im putting it all together and skipping testing too.
So let’s start by adding the report view model that will handle
the report parameters, headers and data sets
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Web;
5: using Microsoft.Reporting.WebForms;
6: using System.Web;
7: using System.Web.Mvc;
8:
9: namespace ASPNET_MVC_RDLC.Models
10: {
11: public class ReportViewModel
12: {
13: public enum ReportFormat { PDF=1,Word=2,Excel=3}
14: public ReportViewModel()
15: {
16: //initation for the data set holder
17: ReportDataSets = new List<ReportDataSet>();
18: }
19:
20: //Name of the report
21: public string Name { get; set; }
22:
23: //Language of the report
24: public string ReportLanguage { get; set; }
25:
26: //Reference to the RDLC file that contain the report definition
27: public string FileName { get; set; }
28:
29: //The main title for the reprt
30: public string ReportTitle { get; set; }
31:
32: //The right and left titles and sub title for the report
33: public string RightMainTitle { get; set; }
34: public string RightSubTitle { get; set; }
35: public string LeftMainTitle { get; set; }
36: public string LeftSubTitle { get; set; }
37:
38: //the url for the logo,
39: public string ReportLogo { get; set; }
40:
41: //date for printing the report
42: public DateTime ReportDate { get; set; }
43:
44: //the user name that is printing the report
45: public string UserNamPrinting { get; set; }
46:
47: //dataset holder
48: public List<ReportDataSet> ReportDataSets { get; set; }
49:
50: //report format needed
51: public ReportFormat Format { get; set; }
52: public bool ViewAsAttachment { get; set; }
53:
54: //an helper class to store the data for each report data set
55: public class ReportDataSet
56: {
57: public string DatasetName { get; set; }
58: public List<object> DataSetData { get; set; }
59: }
60:
61: public string ReporExportFileName { get {
62: return string.Format("attachment; filename={0}.{1}", this.ReportTitle, ReporExportExtention);
63: } }
64: public string ReporExportExtention
65: {
66: get
67: {
68: switch (this.Format)
69: {
70: case ReportViewModel.ReportFormat.Word: return ".doc";
71: case ReportViewModel.ReportFormat.Excel: return ".xls";
72: default:
73: return ".pdf";
74: }
75: }
76: }
77:
78: public string LastmimeType
79: {
80: get
81: {
82: return mimeType;
83: }
84: }
85: private string mimeType;
86: public byte[] RenderReport()
87: {
88: //geting repot data from the business object
89:
90: //creating a new report and setting its path
91: LocalReport localReport = new LocalReport();
92: localReport.ReportPath =System.Web.HttpContext.Current.Server.MapPath(this.FileName);
93:
94: //adding the reort datasets with there names
95: foreach (var dataset in this.ReportDataSets)
96: {
97: ReportDataSource reportDataSource = new ReportDataSource(dataset.DatasetName, dataset.DataSetData);
98: localReport.DataSources.Add(reportDataSource);
99: }
100: //enabeling external images
101: localReport.EnableExternalImages = true;
102:
103: //seting the partameters for the report
104: localReport.SetParameters(new ReportParameter("RightMainTitle", this.RightMainTitle));
105: localReport.SetParameters(new ReportParameter("RightSubTitle", this.RightSubTitle));
106: localReport.SetParameters(new ReportParameter("LeftMainTitle", this.LeftMainTitle));
107: localReport.SetParameters(new ReportParameter("LeftSubTitle", this.LeftSubTitle));
108: localReport.SetParameters(new ReportParameter("ReportTitle", this.ReportTitle));
109: localReport.SetParameters(new ReportParameter("ReportLogo", System.Web.HttpContext.Current.Server.MapPath(this.ReportLogo)));
110: localReport.SetParameters(new ReportParameter("ReportDate", this.ReportDate.ToShortDateString()));
111: localReport.SetParameters(new ReportParameter("UserNamPrinting", this.UserNamPrinting));
112:
113: //preparing to render the report
114:
115: string reportType = this.Format.ToString();
116:
117: string encoding;
118: string fileNameExtension;
119:
120: //The DeviceInfo settings should be changed based on the reportType
121: //http://msdn2.microsoft.com/en-us/library/ms155397.aspx
122: string deviceInfo =
123: "<DeviceInfo>" +
124: " <OutputFormat>" + this.Format.ToString() + "</OutputFormat>" +
125: "</DeviceInfo>";
126:
127: Warning[] warnings;
128: string[] streams;
129: byte[] renderedBytes;
130:
131: //Render the report
132: renderedBytes = localReport.Render(
133: reportType,
134: deviceInfo,
135: out mimeType,
136: out encoding,
137: out fileNameExtension,
138: out streams,
139: out warnings);
140:
141: return renderedBytes;
142: }
143: }
144:
145: }
lets add a new report called MyMVCReport.rdlc as
below:
we will need to add some stub methods to set the report data sources
Set the data source for the report using the stub methods
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Web;
5:
6: namespace ASPNET_MVC_RDLC.Models
7: {
8: public class Business
9: {
10: public List<Employee> StubForEmployeeDataSet()
11: {
12: //this is used only to help in adding the dataset of type employee to the report definition
13: return null;
14: }
15: public List<Project> StubForProjectDataSet()
16: {
17: //this is used only to help in adding the dataset of type project to the report definition
18: return null;
19: }
Note: in production code, I don't recommend putting this in the business layer, any report stub class will be better.
Set the data source for the report using the stub methods
so we have a problem here when selecting add data set from the report menu, the data source drop down list is empty, and when you click new it open a connection to the database.
So after some search I found a solution from stackoverflow, simply add a web form page to the MVC project and it will start working, I didn't have time to check why is this, but it works.
lets add another one.
lets set the design and layout of the report
the above is only for testing.
coming near the last steps , we need to add a reference to the Microsoft web form reports assembly
now lets add the controller that will produce the report.
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Web;
5: using System.Web.Mvc;
6: using ASPNET_MVC_RDLC.Models;
7:
8: namespace ASPNET_MVC_RDLC.Controllers
9: {
10: public class ReportController : Controller
11: {
12: public ActionResult PrintRepert()
13: {
14: //geting repot data from the business object
15: var Business = new Business();
16: var reportViewModel = Business.GetMyRepoertViewModel();
17:
18: var renderedBytes = reportViewModel.RenderReport();
19:
20: if(reportViewModel.ViewAsAttachment)
21: Response.AddHeader("content-disposition", reportViewModel.ReporExportFileName);
22: return File(renderedBytes, reportViewModel.LastmimeType);
23:
24: }
25:
26: }
27: }
Add a link to the page to open the report wherever is needed.
@{
ViewBag.Title = "Home Page";
}
@section
featured {
<section class="featured">
<div class="content-wrapper">
<hgroup class="title">
<h1>@ViewBag.Title.h1>
<h2>@ViewBag.Messageh2>
hgroup>
div>
section>
}
<h3>We suggest the following:h3>
<ol class="round">
<li class="one">
<h5>Print an RDLC report in MVCh5>
@Html.ActionLink("Print
Report", "PrintRepert","Report")
li>
ol>
ok lets run and see.
And its working, there still more work to be done as checking the other formats, the email attachment,
adding some security, the Multilanguage, and uploading this example and linking it here.
I would love to here some comments.
Update: for the business class, it really must come from your original entities. so for this sample this is the one im using
1:
2: namespace ASPNET_MVC_RDLC.Models
3: {
4: public class Business
5: {
6: public List<Employee> StubForEmployeeDataSet()
7: {
8: //this is used only to help in adding the dataset of type employee to the report definition
9: return null;
10: }
11: public List<Project> StubForProjectDataSet()
12: {
13: //this is used only to help in adding the dataset of type project to the report definition
14: return null;
15: }
16: //somew
17: public ReportViewModel GetMyRepoertViewModel()
18: {
19: // I will not go through getting data from ef, i will assume some test data here
20:
21: //first dataset, employee info
22: var EmplyeeDataSet =new List<object>() {
23: new Employee() { Name="Ahmed Khaled", Mobile = "055111111", Email="Ahmed.Khaled@Somewhere.com", BirthDate=new DateTime(1970,10,10)},
24: new Employee() { Name="Jamal Awsom" , Mobile = "055222222", Email="Jamal.Awsom@Somewhere.com", BirthDate=new DateTime(1977,1,10)},
25: new Employee() { Name="James Salem" , Mobile = "055333333", Email="James.Salem@Somewhere.com", BirthDate=new DateTime(1982,12,10)},
26: new Employee() { Name="Tony Read" , Mobile = "055444444", Email="Tony.Read@Somewhere.com", BirthDate=new DateTime(1982,4,10)}
27:
28: };
29: var ProjectDataSet =new List<object>() ;
30: for (int i=1;i<30;i++)
31: {
32: EmplyeeDataSet.Add(new Employee() { Name = "James Salem", Mobile = "055333333",
33: Email = "James.Salem@Somewhere.com", BirthDate = new DateTime(1982, 12, 10) });
34: }
35:
36: //Assuming the person printing the report is me
37: var UserPrinting="Ali Taki";
38:
39: var reportViewModel = new ReportViewModel()
40: {
41: FileName = "~/Reprots/MyMVCReport2.rdlc",
42: LeftMainTitle = "ABC Company Name",
43: LeftSubTitle = "DEF Department Name",
44: RightMainTitle = "اسم الشركة",
45: RightSubTitle = "اسم القسم",
46: Name = "Statistical Report",
47: ReportDate = DateTime.Now,
48: ReportLogo = "~/Content/logo.jpg",
49: ReportTitle = "Summary report for top employee's and projects",
50: ReportLanguage = "en-US",
51: UserNamPrinting = UserPrinting,
52: Format=ReportViewModel.ReportFormat.PDF,
53: ViewAsAttachment=false,
54:
55: };
56: //adding the dataset information to the report view model object
57: reportViewModel.ReportDataSets.Add(new ReportViewModel.ReportDataSet() { DataSetData = EmplyeeDataSet.ToList(), DatasetName = "Employees" });
58: reportViewModel.ReportDataSets.Add(new ReportViewModel.ReportDataSet() { DataSetData = ProjectDataSet.ToList(), DatasetName = "Projects" });
59:
60:
61: return reportViewModel;
62:
63: }
64: }
65: }