[asp.net-mvc-3] How can I use a reportviewer control in an asp.net mvc 3 razor view?

I'm trying to use a reportviewer control, within a razor view, in the mvc 3 framework. The online documentation talks of drag and drop. Any suggestion on how to insert it into the view.

This question is related to asp.net-mvc-3 razor reportviewer

The answer is


There's a MvcReportViewer helper in NuGet.

http://www.nuget.org/packages/MvcReportViewer/

And this is the details:

https://github.com/ilich/MvcReportViewer

I have using this. It works great.


It is possible to get an SSRS report to appear on an MVC page without using iFrames or an aspx page.

The bulk of the work is explained here:

http://geekswithblogs.net/stun/archive/2010/02/26/executing-reporting-services-web-service-from-asp-net-mvc-using-wcf-add-service-reference.aspx

The link explains how to create a web service and MVC action method that will allow you to call the reporting service and render result of the web service as an Excel file. With a small change to the code in the example you can render it as HTML.

All you need to do then is use a button to call a javascript function that makes an AJAX call to your MVC action which returns the HTML of the report. When the AJAX call returns with the HTML just replace a div with this HTML.

We use AngularJS so my example below is in that format, but it could be any javascript function

$scope.getReport = function()
{
    $http({
        method: "POST",
        url: "Report/ExportReport",
        data: 
                [
                    { Name: 'DateFrom', Value: $scope.dateFrom },
                    { Name: 'DateTo', Value: $scope.dateTo },
                    { Name: 'LocationsCSV', Value: $scope.locationCSV }
                ]

    })
    .success(function (serverData)
    {
        $("#ReportDiv").html(serverData);
    });

};

And the Action Method - mainly taken from the above link...

    [System.Web.Mvc.HttpPost]
    public FileContentResult ExportReport([FromBody]List<ReportParameterModel> parameters)
    {
         byte[] output;
         string extension, mimeType, encoding;
         string reportName = "/Reports/DummyReport";
         ReportService.Warning[] warnings;
         string[] ids;

     ReportExporter.Export(
            "ReportExecutionServiceSoap" 
            new NetworkCredential("username", "password", "domain"),
            reportName,
            parameters.ToArray(),
            ExportFormat.HTML4,
            out output,
            out extension,
            out mimeType,
            out encoding,
            out warnings,
            out ids
        );

        //-------------------------------------------------------------
        // Set HTTP Response Header to show download dialog popup
        //-------------------------------------------------------------
        Response.AddHeader("content-disposition", string.Format("attachment;filename=GeneratedExcelFile{0:yyyyMMdd}.{1}", DateTime.Today, extension));
        return new FileContentResult(output, mimeType);
    }

So the result is that you get to pass parameters to an SSRS reporting server which returns a report which you render as HTML. Everything appears on the one page. This is the best solution I could find


You will not only have to use an asp.net page but

If using the Entity Framework or LinqToSql (if using partial classes) move the data into a separate project, the report designer cannot see the classes.

Move the reports to another project/dll, VS10 has bugs were asp.net projects cannot see object datasources in web apps. Then stream the reports from the dll into your mvc projects aspx page.

This applies for mvc and webform projects. Using sql reports in the local mode is not a pleasent development experience. Also watch your webserver memory if exporting large reports. The reportviewer/export is very poorly designed.


This is a simple task. You can follow the following steps.

  1. Create a folder in your solution and name it Reports.
  2. Add an ASP.Net web form and name it ReportView.aspx
  3. Create a Class ReportData and add it to the Reports folder. Add the following code to the Class.

    public class ReportData  
    {  
        public ReportData()  
        {  
            this.ReportParameters = new List<Parameter>();  
            this.DataParameters = new List<Parameter>();  
        }
    
        public bool IsLocal { get; set; }
        public string ReportName { get; set; }
        public List<Parameter> ReportParameters { get; set; }
        public List<Parameter> DataParameters { get; set; }
    }
    
    public class Parameter  
    {  
        public string ParameterName { get; set; }  
        public string Value { get; set; }  
    }
    
  4. Add another Class and name it ReportBasePage.cs. Add the following code in this Class.

    public class ReportBasePage : System.Web.UI.Page
    {
        protected ReportData ReportDataObj { get; set; }
    
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            if (HttpContext.Current != null)
                if (HttpContext.Current.Session["ReportData"] != null)
                {
                    ReportDataObj = HttpContext.Current.Session["ReportData"] as ReportData;
                    return;
                }
            ReportDataObj = new ReportData();
            CaptureRouteData(Page.Request);
        }
    
    
        private void CaptureRouteData(HttpRequest request)
        {
            var mode = (request.QueryString["rptmode"] + "").Trim();
            ReportDataObj.IsLocal = mode == "local" ? true : false;
            ReportDataObj.ReportName = request.QueryString["reportname"] + "";
            string dquerystr = request.QueryString["parameters"] + "";
            if (!String.IsNullOrEmpty(dquerystr.Trim()))
            {
                var param1 = dquerystr.Split(',');
                foreach (string pm in param1)
                {
                    var rp = new Parameter();
                    var kd = pm.Split('=');
                    if (kd[0].Substring(0, 2) == "rp")
                    {
                        rp.ParameterName = kd[0].Replace("rp", "");
                        if (kd.Length > 1) rp.Value = kd[1];
                        ReportDataObj.ReportParameters.Add(rp);
                    }
                    else if (kd[0].Substring(0, 2) == "dp")
                    {
                        rp.ParameterName = kd[0].Replace("dp", "");
                        if (kd.Length > 1) rp.Value = kd[1];
                        ReportDataObj.DataParameters.Add(rp);
                    }
                }
            }
        }
    }
    
  5. Add ScriptManager to the ReportView.aspx page. Now add a Report Viewer to the page. In report viewer set the property AsyncRendering="false". The code is given below.

        <rsweb:ReportViewer ID="ReportViewerRSFReports" runat="server" AsyncRendering="false"
            Width="1271px" Height="1000px" >
        </rsweb:ReportViewer>
    
  6. Add two NameSpace in ReportView.aspx.cs

    using Microsoft.Reporting.WebForms;
    using System.IO;
    
  7. Change the System.Web.UI.Page to ReportBasePage. Just replace your code using the following.

    public partial class ReportView : ReportBasePage
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                RenderReportModels(this.ReportDataObj);
            }
        }
    
        private void RenderReportModels(ReportData reportData)
        {
            // This is the Data Access Layer from which a method is called to fill data to the list.
            RASolarERPData dal = new RASolarERPData();
            List<ClosingInventoryValuation> objClosingInventory = new List<ClosingInventoryValuation>();
    
            // Reset report properties.
            ReportViewerRSFReports.Height = Unit.Parse("100%");
            ReportViewerRSFReports.Width = Unit.Parse("100%");
            ReportViewerRSFReports.CssClass = "table";
    
            // Clear out any previous datasources.
            this.ReportViewerRSFReports.LocalReport.DataSources.Clear();
    
            // Set report mode for local processing.
            ReportViewerRSFReports.ProcessingMode = ProcessingMode.Local;
    
            // Validate report source.
            var rptPath = Server.MapPath(@"./Report/" + reportData.ReportName +".rdlc");
    
            //@"E:\RSFERP_SourceCode\RASolarERP\RASolarERP\Reports\Report\" + reportData.ReportName + ".rdlc";
            //Server.MapPath(@"./Report/ClosingInventory.rdlc");
    
            if (!File.Exists(rptPath))
                return;
    
            // Set report path.
            this.ReportViewerRSFReports.LocalReport.ReportPath = rptPath;
    
            // Set report parameters.
            var rpPms = ReportViewerRSFReports.LocalReport.GetParameters();
            foreach (var rpm in rpPms)
            {
                var p = reportData.ReportParameters.SingleOrDefault(o => o.ParameterName.ToLower() == rpm.Name.ToLower());
                if (p != null)
                {
                    ReportParameter rp = new ReportParameter(rpm.Name, p.Value);
                    ReportViewerRSFReports.LocalReport.SetParameters(rp);
                }
            }
    
            //Set data paramater for report SP execution
            objClosingInventory = dal.ClosingInventoryReport(this.ReportDataObj.DataParameters[0].Value);
    
            // Load the dataSource.
            var dsmems = ReportViewerRSFReports.LocalReport.GetDataSourceNames();
            ReportViewerRSFReports.LocalReport.DataSources.Add(new ReportDataSource(dsmems[0], objClosingInventory));
    
            // Refresh the ReportViewer.
            ReportViewerRSFReports.LocalReport.Refresh();
        }
    }
    
  8. Add a Folder to the Reports Folder and name it Report. Now add a RDLC report to the Reports/Report folder and name it ClosingInventory.rdlc.

  9. Now add a Controller and name it ReportController. In the controller add the following action method.

    public ActionResult ReportViewer()
        {                
            ViewData["reportUrl"] = "../Reports/View/local/ClosingInventory/";
    
            return View();
        }
    
  10. Add a view page click on the ReportViewer Controller. Name the view page ReportViewer.cshtml. Add the following code to the view page.

    @using (Html.BeginForm("Login"))
     { 
           @Html.DropDownList("ddlYearMonthFormat", new SelectList(ViewBag.YearMonthFormat, "YearMonthValue",
     "YearMonthName"), new { @class = "DropDown" })
    
    Stock In Transit: @Html.TextBox("txtStockInTransit", "", new { @class = "LogInTextBox" })
    
    <input type="submit" onclick="return ReportValidationCheck();" name="ShowReport"
                     value="Show Report" />
    
    }
    
  11. Add an Iframe. Set the property of the Iframe as follows

    frameborder="0"  width="1000"; height="1000"; style="overflow:hidden;"
    scrolling="no"
    
  12. Add Following JavaScript to the viewer.

    function ReportValidationCheck() {
    
        var url = $('#hdUrl').val();
        var yearmonth = $('#ddlYearMonthFormat').val();      
        var stockInTransit = $('#txtStockInTransit').val()
    
        if (stockInTransit == "") {
            stockInTransit = 0;
        }
    
        if (yearmonth == "0") {
            alert("Please Select Month Correctly.");
        }
        else {
    
            //url = url + "dpSpYearMonth=" + yearmonth + ",rpYearMonth=" + yearmonth + ",rpStockInTransit=" + stockInTransit;
    
            url = "../Reports/ReportView.aspx?rptmode=local&reportname=ClosingInventory&parameters=dpSpYearMonth=" + yearmonth + ",rpYearMonth=" + yearmonth + ",rpStockInTransit=" + stockInTransit;
    
            var myframe = document.getElementById("ifrmReportViewer");
            if (myframe !== null) {
                if (myframe.src) {
                    myframe.src = url;
                }
                else if (myframe.contentWindow !== null && myframe.contentWindow.location !== null) {
                    myframe.contentWindow.location = url;
                }
                else { myframe.setAttribute('src', url); }
            }
        }
    
        return false;
    }
    
  13. Web.config file add the following key to the appSettings section

    add key="UnobtrusiveJavaScriptEnabled" value="true"
    
  14. In system.web handlers Section add the following key

    add verb="*" path="Reserved.ReportViewerWebControl.axd" type = "Microsoft.Reporting.WebForms.HttpHandler, Microsoft.ReportViewer.WebForms, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
    
  15. Change your data source to your own. This solution is very simple and I think every one will enjoy it.


Here is the complete solution for directly integrating a report-viewer control (as well as any asp.net server side control) in an MVC .aspx view, which will also work on a report with multiple pages (unlike Adrian Toman's answer) and with AsyncRendering set to true, (based on "Pro ASP.NET MVC Framework" by Steve Sanderson).

What one needs to do is basically:

  1. Add a form with runat = "server"

  2. Add the control, (for report-viewer controls it can also sometimes work even with AsyncRendering="True" but not always, so check in your specific case)

  3. Add server side scripting by using script tags with runat = "server"

  4. Override the Page_Init event with the code shown below, to enable the use of PostBack and Viewstate

Here is a demonstration:

<form ID="form1" runat="server">
    <rsweb:ReportViewer ID="ReportViewer1" runat="server" />
</form>
<script runat="server">
    protected void Page_Init(object sender, EventArgs e)
    {
        Context.Handler = Page;
    }
    //Other code needed for the report viewer here        
</script>

It is of course recommended to fully utilize the MVC approach, by preparing all needed data in the controller, and then passing it to the view via the ViewModel.

This will allow reuse of the View!

However this is only said for data this is needed for every post back, or even if they are required only for initialization if it is not data intensive, and the data also has not to be dependent on the PostBack and ViewState values.

However even data intensive can sometimes be encapsulated into a lambda expression and then passed to the view to be called there.

A couple of notes though:

  • By doing this the view essentially turns into a web form with all it's drawbacks, (i.e. Postbacks, and the possibility of non Asp.NET controls getting overriden)
  • The hack of overriding Page_Init is undocumented, and it is subject to change at any time

I am using ASP.NET MVC3 with SSRS 2008 and I couldn't get @Adrian's to work 100% for me when trying to get reports from a remote server.

Finally, I found that I needed to change the Page_Load method in ViewUserControl1.ascx to look like this:

ReportViewer1.ProcessingMode = ProcessingMode.Remote;
ServerReport serverReport = ReportViewer1.ServerReport;
serverReport.ReportServerUrl = new Uri("http://<Server Name>/reportserver");
serverReport.ReportPath = "/My Folder/MyReport";
serverReport.Refresh();

I had been missing the ProcessingMode.Remote.

References:

http://msdn.microsoft.com/en-us/library/aa337091.aspx - ReportViewer


the documentations refers to an ASP.NET application.
You can try and have a look at my answer here.
I have an example attached to my reply.
Another example for ASP.NET MVC3 can be found here.


Examples related to asp.net-mvc-3

Better solution without exluding fields from Binding IIs Error: Application Codebehind=“Global.asax.cs” Inherits=“nadeem.MvcApplication” Can we pass model as a parameter in RedirectToAction? return error message with actionResult Why is this error, 'Sequence contains no elements', happening? Failed to load resource: the server responded with a status of 500 (Internal Server Error) in Bind function 500.19 - Internal Server Error - The requested page cannot be accessed because the related configuration data for the page is invalid String MinLength and MaxLength validation don't work (asp.net mvc) How to set the value of a hidden field from a controller in mvc How to set a CheckBox by default Checked in ASP.Net MVC

Examples related to razor

Uncaught SyntaxError: Invalid or unexpected token How to pass a value to razor variable from javascript variable? error CS0103: The name ' ' does not exist in the current context how to set radio button checked in edit mode in MVC razor view @Html.DropDownListFor how to set default value Razor MVC Populating Javascript array with Model Array How to add "required" attribute to mvc razor viewmodel text input editor How to correctly use Html.ActionLink with ASP.NET MVC 4 Areas Multiple radio button groups in MVC 4 Razor How to hide a div element depending on Model value? MVC

Examples related to reportviewer

Missing Microsoft RDLC Report Designer in Visual Studio Microsoft.ReportViewer.Common Version=12.0.0.0 Could not load file or assembly 'Microsoft.ReportViewer.WebForms' How can I use a reportviewer control in an asp.net mvc 3 razor view? SSRS 2008 R2 - SSRS 2012 - ReportViewer: Reports are blank in Safari and Chrome Creating a PDF from a RDLC Report in the Background ReportViewer Client Print Control "Unable to load client print control"?