- Overview
- Requirement Specifications/Prerequisites
- Design Specification
- Create RDLC Report Design
- Model View Controller
- Improvement Areas
- References
1. Overview
The intent of this article is to help fellow MVC developer to solve the problem related to Reportviewer in MVC Razor framework. To be candid, its an attempt! I searched online to have ways/approach to incorporate Reportviewer in MVC Razor , but all search ends up stating-' no alternative'. I came up with small prototype wherein we can create and build search based report similar to somewhat ReportViewer component.
2. Problem Statement /Prerequisites
We require simple filter based report build in MVC Razor. There will be input search filter criteria to display reports in tabular format.
To built solution, we used VS2012 and MVC4 Razor Architecture.
3. Design Specifications
- Design RDLC Report .
- Create Domain-Model **SearchParameterModel and ***WorldReportModel. SearchParameterModel is class object to store report filter criteria as entered by user. This search parameter then passed to controller on HTTPost triggered by button click event. WorldModel is used to store records from data source.
- Create View ReportViewer.chtml.
- Create Controller ReportViewerController
What we get?
- With this solution we can have flexibility of using stored procedure output(Bind RDLC with datasource of RDLC) with required inputs from MVC razor view.
- With this solution we can download report in pDF, word or excel
- With this solution we can make use of
img src="@Url.Action("GenerateAndDisplayReport", "Report", new { territory = Model.Territory, format = Model.Format })" to render report in web
4. Create RDLC Report Design
1. Add new item report- Report1.RDLC in the solution.
2. Add new item-dataset -Dataset1.xsd. Manually add column in the datatable using designer view. The column name should match with the public property in Models-ReportModels(Class SearchParameterModel).Any deviation results in error.
When above steps are completed...Check below
3. Click on RDLC file drag and drop required Tablix control and populate with required data set fields. In order to do this, click on Menu-View-Reportdata. Add manually New Dataset , this form will provide you the option to select the dataset1 that we created initially.
For more help. Refer my article.Design Report data Structure in RDLC
Fig1. Create dataaset1 schema for RDLC report datasource mapping.
Fig 2. We can reference this dataset1 in Report data as shown in sanpshot below. Right click and add new dataset.
5. Model View Controller
Model: ReportModels
The SearchParameterModel object act as placeholder to accept input parameter to filter report result set. Also below class helps in displaying content of labels as well as used validating input fields. The format property is used to identify the report format as PDF, word or excel.
public class SearchParameterModel
{
[Display(Name = "Search By Terrritory")]
public string Territory
{
get ;
set ;
}
public string Format
{
get;
set;
}
}
WorldModel class is data structure of report result set. This will be later populate and bind report RDLC data source. One warning, do define private members in getter and setter properties otherwise RDLC dataset won't be able to identify the fields or column .It results into report error.
public class WorldModel
{
private string m_Territory;
private string m_Country;
private string m_Year;
private string m_Stats;
public string Territory
{
get {return m_Territory ; }
set {value = m_Territory;}
}
public string Country
{
get { return m_Country; }
set { value = m_Country; }
}
public string Year
{
get { return m_Year; }
set { value = m_Year; }
}
public string Stats
{
get { return m_Stats; }
set { value = m_Stats; }
}
public WorldModel() { }
public WorldModel(string territory, string country, string year, string stats)
{
m_Territory = territory;
m_Year = year;
m_Country = country;
m_Stats = stats;
}
}
View
1. The view is simple form with input text with one button. We have image tag to rendered report in section. On button click we passed input values to controller using HttpPost-ActionResult
2. View is coupled with Strongly typed Model -->ViewModel . @model MvcApplication3.Models.SearchParameterModel
3. Container :We capture/store textfield value using @Html.TextBoxFor(m => m.Territory)
4. On button click we check Model collection object is populated or exist by invoking controller public ViewResult ReportViewer(SearchParameterModel um)
.This is called when httppost occured when button clicked.
5. Now the ***imp twist is , we now get value of input parameter back to view and is initialized in @Url.Action("GenerateAndDisplayReport", "Report", new { territory = Model.Territory, format = Model.Format }).. Check the @ if(Model !=null)
The above workflow is bit tedious to understand at this point of time. All you need is to download source code and understand the flow of code execution. MVC is all about concepts and practice.
Controller
1. The ActionResult ReportViewer()
is invoked for the first time on page load of Report form. i.e When we clicked onMenu report link.
2. The ActionResult ReportViewer(SearchParameterModel um) is invoked when form post takes place due to button clicked event.
3. FileContentResult GenerateAndDisplayReport(string territory, string format) is used to bind and populate img with response stream. Check view @URl.Action for more details.
public class ReportController : Controller
{
[AllowAnonymous]
public ActionResult ReportViewer(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ViewResult ReportViewer(SearchParameterModel um)
{
return View(um);
}
public FileContentResult GenerateAndDisplayReport(string territory, string format)
{
LocalReport localReport = new LocalReport();
localReport.ReportPath = Server.MapPath("~/Content/Report1.rdlc");
IList<WorldModel> customerList = new List<WorldModel>();
customerList.Add(new WorldModel("Europe", "Sweden", "2001", "1823"));
customerList.Add(new WorldModel("Europe", "Sweden", "2002", "1234"));
customerList.Add(new WorldModel("Europe", "Sweden", "2003", "9087"));
customerList.Add(new WorldModel("Europe", "Denmark", "2001", "6793"));
customerList.Add(new WorldModel("Europe", "Denmark", "2002", "4563"));
customerList.Add(new WorldModel("Europe", "Denmark", "2003", "1897"));
customerList.Add(new WorldModel("Europe", "Norway", "2001", "5632"));
customerList.Add(new WorldModel("Europe", "Norway", "2002", "9870"));
customerList.Add(new WorldModel("Europe", "Norway", "2003", "2367"));
customerList.Add(new WorldModel("Asia", "India", "2001", "1980"));
customerList.Add(new WorldModel("Asia", "India", "2002", "9765"));
customerList.Add(new WorldModel("Asia", "India", "2003", "6789"));
customerList.Add(new WorldModel("Asia", "Japan", "2001", "9871"));
customerList.Add(new WorldModel("Asia", "Japan", "2002", "2987"));
customerList.Add(new WorldModel("Asia", "Japan", "2003", "1256"));
customerList.Add(new WorldModel("North America", "United States", "2001", "9871"));
customerList.Add(new WorldModel("North America", "United States", "2002", "9871"));
customerList.Add(new WorldModel("North America", "United States", "2003", "9871"));
customerList.Add(new WorldModel("North America", "Canada", "2001", "9871"));
customerList.Add(new WorldModel("North America", "Canada", "2002", "9871"));
customerList.Add(new WorldModel("North America", "Canada", "2003", "9871"));
customerList.Add(new WorldModel("North America", "Mexico", "2001", "9871"));
customerList.Add(new WorldModel("North America", "Mexico", "2002", "9871"));
customerList.Add(new WorldModel("North America", "Mexico", "2003", "9871"));
customerList.Add(new WorldModel("South America", "Brazil", "2001", "9871"));
customerList.Add(new WorldModel("South America", "Brazil", "2002", "9871"));
customerList.Add(new WorldModel("South America", "Brazil", "2003", "9871"));
customerList.Add(new WorldModel("South America", "Columbia", "2001", "9871"));
customerList.Add(new WorldModel("South America", "Columbia", "2002", "9871"));
customerList.Add(new WorldModel("South America", "Columbia", "2003", "9871"));
customerList.Add(new WorldModel("South America", "Argentina", "2001", "9871"));
customerList.Add(new WorldModel("South America", "Argentina", "2002", "9871"));
customerList.Add(new WorldModel("South America", "Argentina", "2003", "9871"));
ReportDataSource reportDataSource = new ReportDataSource();
reportDataSource.Name = "DataSet1";
if (territory != null)
{
var customerfilterList = from c in customerList
where c.Territory == territory
select c;
reportDataSource.Value = customerfilterList;
}
else
reportDataSource.Value = customerList;
localReport.DataSources.Add(reportDataSource);
string reportType = "Image";
string mimeType;
string encoding;
string fileNameExtension;
Warning[] warnings;
string[] streams;
byte[] renderedBytes;
renderedBytes = localReport.Render(reportType, deviceInfo, out mimeType, out encoding, out fileNameExtension, out streams, out warnings);
if (format == null)
{
return File(renderedBytes, "image/jpeg");
}
else if (format == "PDF")
{
return File(renderedBytes, "pdf");
}
else {
return File(renderedBytes, "image/jpeg");
}
} }
6.Improvement Areas
The only problem with this solution is , it can not replace existing report viewer. The above solution is not robust as it rendered report as image in web form Razor view.
This need to be looked upon and html rendering option must be explored which I failed to achieve.Perhaps powerful web control like Report Viewer is missing in mvc razor architecture.
7. References
Report RDLC Design Tutorial