Viewing SSRS Reports in an ASP.net MVC Site

C#asp.net MvcReportviewerReporting Services

C# Problem Overview


Is there a way to put a SQL Server Reporting Services report viewer control on an ASP.net MVC view? If not...what is the best way to accomplish this?

C# Solutions


Solution 1 - C#

No, not in a MVC view. But you can have a web forms pages which have server controls in them mixed in with your MVC site.

Hmm, just googled "mix asp.net mvc and web forms" to find some examples, and google questioned whether I'm human or not :)

Anyway, here's a link - http://www.packtpub.com/article/mixing-asp.net-webforms-and-asp.net-mvc - there's a few out there. I've also done this in a MVC site for the same reason - the report control.

Solution 2 - C#

No, the ReportViewer control won't work if you place it in an MVC view, since it requires ViewState. You'll have to create an old-school web form and put the ReportViewer there instead.

A solution I used in a project I worked on was to create a custom route handler, so I could still make use of URL routing. The route handler would take parameters like the report name from the RouteData collection, create an instance of my web form, and pass the parameters to it via public properties. The web form would read these in Page_Load and configure the ReportViewer control.

// Configure a route in Global.asax.cs that is handled by a ReportRouteHandler
routes.Add("ReportRoute", new Route("Reports/{reportName}",
                                    new ReportRouteHandler());

public class ReportRouteHandler : IRouteHandler {
    public IHttpHandler GetHttpHandler(RequestContext requestContext) {
        var reportName = requestContext.RouteData.Values["reportName"] as string;
        
        var webform = BuildManager
            .CreateInstanceFromVirtualPath("~/Path/To/ReportViewerWebForm.aspx",
                                           typeof(Page)) as ReportViewerWebForm;
        webform.ReportToShow = reportName;
        return webform;
    }
}

This code is just a starting point if you decide to use this approach, of course. The one I created also did some user authentication and parameter validation before returning.

Update: Looks like if you're using ASP.NET 4.0, most of this can be done automatically!

Solution 3 - C#

Implementing a SSRS ReportViewer control in MVC consists of two problems:

  1. Minimally, you'll need to add the right dependencies, handlers, and configuration for the ReportViewer control (regardless of project type).
  2. The trickier hurdle is in Mixing WebForms and MVC. We need a way of rendering and routing incoming requests so they will be handled by WebForms pages, controls, and actions.

Problem 1 - Configuring the ReportViewer

If you've done a lot with setting up ReportViewer controls in the past, this might be old hat and you can skip to section 2.

  1. Add package/reference - The [ReportViewer][1] control lives in the Microsoft.ReportViewer.WebForms.dll. You can include in your project by adding the [Microsoft.ReportViewer.WebForms][2] package from nuget:

[![Nuget - Microsoft.ReportViewer.WebForms][3]][3]

  1. Web.config Handlers - Per this article on [Web.config Settings for ReportViewer][4], and [this SO question][5] you'll need to add the following to your web.config:

     <system.web>
       <httpHandlers>
         <add verb="*" path="Reserved.ReportViewerWebControl.axd" 
              type="Microsoft.Reporting.WebForms.HttpHandler,
                    Microsoft.ReportViewer.WebForms, Version=10.0.0.0, Culture=neutral,
                    PublicKeyToken=b03f5f7f11d50a3a" />
       </httpHandlers>
     </system.web>
     <system.webServer>
       <handlers>
         <remove name="ReportViewerWebControlHandler" />
         <add name="ReportViewerWebControlHandler" preCondition="integratedMode"
              verb="*" path="Reserved.ReportViewerWebControl.axd" 
              type="Microsoft.Reporting.WebForms.HttpHandler, 
                    Microsoft.ReportViewer.WebForms, Version=10.0.0.0, Culture=neutral,
                    PublicKeyToken=b03f5f7f11d50a3a"/>
       </handlers>
     </system.webServer>
    

Per [this question on duplicate keys][6], it's typically easiest to remove and then re-add webserver configs

  1. Fix broken Image Requests - there's a known defect in ReportViewer with [blank.gif images not loading][7] so you can add the following fix to your global.asax.cs:

     protected void Application_BeginRequest(object sender, EventArgs e)
     {
         HttpRequest req = HttpContext.Current.Request;
         if (req.Url.PathAndQuery.StartsWith("/Reserved.ReportViewerWebControl.axd") &&
             !req.Url.ToString().ToLower().Contains("iteration") &&
             !String.IsNullOrEmpty(req.QueryString["ResourceStreamID"]) &&
             req.QueryString["ResourceStreamID"].ToLower().Equals("blank.gif"))
         {
             Context.RewritePath(String.Concat(req.Url.PathAndQuery, "&IterationId=0"));
         }
     }
    
  2. IgnoreRoute .axd - If it's not already there, make sure to [allow ScriptResources][8] in your RouteConfig.cs:

     routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    
  3. Add ReportViewerPage.aspx - Add a WebForm page that will hold an instance of the ReportViewer control. In order to work, that control needs to find a ScriptManager control and be placed inside of a <form runat="server" >.
    So your new .aspx page should look something like this:

     <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ReportViewerPage.aspx.cs" Inherits="MVCAppWithReportViewer.ReportViewerPage" %>
     <%@ Register TagPrefix="rsweb" Namespace="Microsoft.Reporting.WebForms" Assembly="Microsoft.ReportViewer.WebForms, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" %>
     
     <!DOCTYPE html>
     <html xmlns="http://www.w3.org/1999/xhtml">
     <head runat="server">
         <title>Report Viewer</title>
     </head>
     <body>
         <form id="form1" runat="server">
             <rsweb:ReportViewer ID="ReportViewer" runat="server" 
                                 Height="100%" Width="100%" 
                                 SizeToReportContent="True" ProcessingMode="Remote" />
             <asp:ScriptManager ID="ScriptManager1" runat="server" />
         </form>
     </body>
     </html>
    
  4. Wire up ReportViewer on Page_Load - Assuming, you already have an SSRS report fully deployed to a reporting server which is available at an address like this:

    http://ReportServerName/Reports/Pages/Report.aspx?ItemPath=%2fCompany%2fClientReport

    Then your code-behind in your new WebForm page should look like this:

     public partial class ReportViewerPage : System.Web.UI.Page
     {
         protected void Page_Load(object sender, EventArgs e)
         {
             if (!Page.IsPostBack)
             {
                 // confirm report properties (also setable in attributes)
                 ReportViewer.ProcessingMode = ProcessingMode.Remote;
                 
                 // config variables
                 var reportServer = "ReportServerName";
                 var reportPath = "/Company/";
                 var reportName = "ClientReport";    
    
                 // report setup
                 var serverReport = new ServerReport();
                 serverReport = ReportViewer.ServerReport;
                 serverReport.ReportServerUrl = new Uri($@"http://{reportServer}/ReportServer");
                 serverReport.ReportPath = $@"{reportPath}{reportName}";
                 
                 // report input
                 var parameters = new List<ReportParameter>();
                 parameters.Add(new ReportParameter("User_uid", "1"));
                 serverReport.SetParameters(parameters);
    
                 // run report
                 serverReport.Refresh();
             }
         }
     }
    
  5. View Report - At this point you should be able to view your report on it's own by selecting View in Browser or Ctrl + Shift + W

[![View in Browser][9]][9]

Problem 2 - Mixing WebForms and MVC

First, let's quickly dissect the routing differences between how these controls are loaded and subsequently updated

  • MVC routes will look something like this {controller}/{action}/{id} where the routing engine will automatically find a Controller and Action with the specified name and incoming requests will be handled by that method. On any page request, whether from page load, form submit, button clicks, anchor navigation, or ajax calls, the exact method being executed is always specified in the url {action}.

  • WebForms routes to code by finding the physical .aspx page address, and then uses ViewState & PostData to wire up and fire events on that page / control.

    Here's an [illustration of different routing formats in WebForms][10]. And here's a simple button click event which will submit a post back to the parent page and raise the appropriate events within the page based on the event data submitted:

    [![ASP.NET WebForms - Postback][11]][12]

This is a pretty big constraint on our solutions available. Nothing is special about the ReportViewer control. It's just a sophisticated set of UserControl classes that respond to click and other input events by posting back the current address along with the ViewState and Event info. So whatever assumptions were baked into the routing and navigation of the ReportViewer will need to persist into our MVC wrapper.

  1. Option 1 - Add Route for .aspx page

As of MVC 4.0+, you can use [URL Routing with WebForms][13]. This mixes well with MVC by adding a [MapPageRoute][14] (note the Page part) to map a route to a physical file. So add the following to your RouteConfig.cs:

<!-- language: lang-cs -->

    routes.MapPageRoute(
        routeName: "ReportViewer",
        routeUrl: "ReportViewer/{reportName}",
        physicalFile: "~/ReportViewerPage.aspx"
    );

The report will run when you navigate to the address ~/Reports/reportName. This will probably be invoked from inside a controller action, perhaps with some user entered parameters or web.config connection strings. There are lots of [ways to manage state in ASP.NET][19] and [Pass Values to ASP.NET Web Forms Pages][20]. One option would be to stash the info in the Session and Redirect like this in your controller:

<!-- language: lang-cs -->

    HttpContext.Session[reportSetup.ReportName] = new ReportSetup() {ReportName = "ClientReport"}; //reportSetup;}
    return RedirectToRoute("ReportViewer", new { reportName = reportSetup.ReportName});

Then, inside the .aspx page, and you can grab the reportName from the RouteData Values and any setup params from the session:

<!-- language: lang-cs -->

    // get report name from route
    string reportName = Page.RouteData.Values["reportName"].ToString();

    // get model from session and clear
    ReportSetup setup = (ReportSetup)HttpContext.Current.Session[reportName];

Pros:

  • Most of the routing seems to work by default, and AJAX controls work fine, so you can set AyncRendering=True

Cons:

  • It's hard to [use an ASP Web Form with a Razor MVC Layout][22] so rendering will take users out of the flow of the rest of the application.
  • Also, report values have to be exposed as part of the URL or passed indirectly via session (as opposed to hydrating directly onto the object).
  1. Option 2 - Nest .ascx inside PartialView on your Page

Adapted from [How can I use a ReportViewer control with Razor?][21], you can consume .ascx controls in PartialViews as long as they inherit from [System.Web.Mvc.ViewUserControl][23].

Create a new Web Forms User Control called ReportViewerControl.ascx that looks like this:

<!-- language: lang-xml -->


<pre><code>&lt;%@ Control Language="C#" AutoEventWireup="true" CodeBehind="ReportViewerControl.ascx.cs" Inherits="MVCAppWithReportViewer.ReportViewerControl" %&gt;
&lt;%@ Register TagPrefix="rsweb" Namespace="Microsoft.Reporting.WebForms" Assembly="Microsoft.ReportViewer.WebForms, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" %&gt;

&lt;form id="form1" runat="server"&gt;
    &lt;rsweb:ReportViewer ID="ReportViewer" runat="server" 
                        Height="100%" Width="100%"  
                        SizeToReportContent="True" ProcessingMode="Remote"
                        <b>AsyncRendering="False"</b> /&gt;
    &lt;asp:ScriptManager ID="ScriptManager1" runat="server" 
                       <b>EnablePartialRendering="false"</b>  /&gt;
&lt;/form&gt;
</code></pre>

> Note: You must set AsyncRendering="False" and EnablePartialRendering="false"

In the code behind you'll need to replace the inheritance type from System.Web.UI.UserControl to System.Web.Mvc.ViewUserControl.

And on Page_Init, you'll need to set the [Context.Handler][24] to Page so events are registered properly.

So the ReportViewerControl.ascx.cs should look like this:

<!-- language: lang-cs -->

    public partial class ReportViewerControl : System.Web.Mvc.ViewUserControl
    {
        protected void Page_Init(object sender, EventArgs e)
        {
            // Required for report events to be handled properly.
            Context.Handler = Page;
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                /* ... report setup ... */ 
                serverReport.Refresh();
            }
        }
    }

In order to render the report, add the following to your controller view:

<!-- language: lang-cs -->

    @Html.Partial("ReportViewerControl", Model)

And then in the ReportViewerControl.ascx.cs Page_Load event, you can retrieve the passed in model from the [ViewUserControl.Model][26] property like this:

<!-- language: lang-cs -->

    ReportSetup setup = (ReportSetup)Model;

Pros:

  • Can build into master _layout.cshtml and consume in regular views
  • Can pass model directly

Cons:

  • AsyncRendering must be set to false, so interactions like pagination and sorting cause full page refreshes and are sometimes wonky. Brian Hartman's has a blog just for ReportViewer and talks about [AsyncRendering and all the Baggage that Comes With It][25].

Further Reading:

Solution 4 - C#

Now there's a MvcReportViewer helper. We can get it from NuGet.

Project Site on GitHub

NuGet Package

Solution 5 - C#

This is a bit simple and will require a bit of fixing to pass something decent to a view in MVC

public ActionResult Index()
{
    /*Credentials of a user that has access to SSRS*/
    string userid = "UserId";
    string password = "MyPassword";
    string domain = "MyDomain";

    string reportURL="http://ServerName/ReportServer?/ReportsFolder/ReportName&Parameter=UserName&rs:Command=Render&rs:Format=PDF";

    NetworkCredential nwc = new NetworkCredential(userid, password, domain);

    WebClient client = new WebClient();
    client.Credentials = nwc;

    Byte[] pageData = client.DownloadData(reportURL);

    Response.ContentType = "application/pdf";
    Response.AddHeader("Content-Disposition", "attachment; filename=" + DateTime.Now);
    Response.BinaryWrite(pageData);
    Response.Flush();
    Response.End();
            
    //return View();
    }

Solution 6 - C#

A simple solution is to add an iframe to your MVC view that opens the report you want from the reporting services web service. The iframe will be fully operational with the components from reporting services. The parameters used for the url in the iframe can also be controlled dynamically (e.g. with ajax) if you want to move the components out into your MVC view.

Although this works, you will still have to sign in to the web reporting service (the iframe will open a logon dialog). For IE this is "automagically" done though using your windows logon credentials.

Solution 7 - C#

You can view report in MVC using ReportViewerForMvc, by installing it using Nuget

Install-Package Microsoft.Report.Viewer -Version 11.0.0

Install-Package Microsoft.ReportViewer.Runtime.WebForms -Version 12.0.2402.15

Install-Package ReportViewerForMvc

Once you have installed the ReportViewer and other required Nuget packages as showed above, Add a new Report.rdlc in your Visual Studio project

enter image description here

Add dataset in the above created report.rdlc

Now, create a ActionMethod in MVC, which will query data from database and return report

 SSRSInMVC.Report.Report ds = new SSRSInMVC.Report.Report();
    public ActionResult ReportStudent()
    {
        ReportViewer reportViewer = new ReportViewer();
        reportViewer.ProcessingMode = ProcessingMode.Local;
        reportViewer.SizeToReportContent = true;
        reportViewer.Width = Unit.Percentage(900);
        reportViewer.Height = Unit.Percentage(900);

        var connectionString = ConfigurationManager.ConnectionStrings["SSRSInMVC.Properties.Settings.StudentsConnectionString"].ConnectionString;


        SqlConnection conx = new SqlConnection(connectionString);
        SqlDataAdapter adp = new SqlDataAdapter("SELECT * FROM Student_details", conx);

        adp.Fill(ds, ds.Student_details.TableName);

        reportViewer.LocalReport.ReportPath = Request.MapPath(Request.ApplicationPath) + @"Report\Report1.rdlc";
        reportViewer.LocalReport.DataSources.Add(new ReportDataSource("DataSet1", ds.Tables[0]));


        ViewBag.ReportViewer = reportViewer;

        return View();
    }

In the view, you have code as below

@using ReportViewerForMvc;
@{
   ViewBag.Title = "Report Student";
 }
 <br/>
<br />

   @Html.ReportViewer(ViewBag.ReportViewer as Microsoft.Reporting.WebForms.ReportViewer)

That's it, we are done.

Reference: https://qawithexperts.com/article/asp.net/displaying-ssrs-sql-server-reporting-service-in-mvc-view/77

Solution 8 - C#

Just in case it helps anybody, these video tutorials are what I found to be easy to follow.
(You just have to tolerate the horrible background music in the first video.)

SSRS 2019 Report in ASP Net MVC 5

How To Filter SSRS 2019 Report Using Parameter

I had to install "ReportViewerForMvc14" instead of "ReportViewerForMvc" (which was used in the video), because it is not available anymore. The note on the package says it is the same as the original but just updated to work with ReportViewer 14.0.

Solution 9 - C#

I have faced a lot of issues setting up the ssrs reports in asp.net mvc . Hope this solution helps. i'm using vb as programming language in the code behind.

Note: This is setting up ssrs report server side. Assuming ssrs report is already published on the remote ssrs server.

  1. Installing reportviewer package on to your solution. I'm making use of nuget package manager console for setting up the dependent files required for the solution. I have installed ReportViewerForMvc14 for my solution and i'm using .net framework 4.7.2 (as shown in the screenshot below)

Note:- Install-Package ReportViewerForMVC didn't work ..I had included 14 at the end .

Go to Tools--> Nuget Package Manager --> Package Manager Console--> Select the default Project --> Run the command 'Install-Package ReportViewerForMvc14'.

enter image description here

  1. The above command will add the supporting files , dlls needed for the solution. Also after installing check 'ReportViewerForMvc' is added onto the references.

  2. Including the below code snippet in the controller. After setting all the properties of the report viewer, i'm storing the report viewer content in viewbag


Function Index() As ActionResult
'Fetch ssrs report server url and case history folder path entries from config.
Dim ssrsReportServerUrl As String = ConfigurationManager.AppSettings.Get("SSRSReportURL")
Dim caseHistoryFolderPath As String = ConfigurationManager.AppSettings.Get("SSRSCaseHistoryReportPath")
Dim qsCaseId As String = "CaseID"
Dim CaseId As Integer = 0
If String.IsNullOrWhiteSpace(Request.QueryString(qsCaseId)) Then
Throw New ArgumentNullException("Page did not receive Case Id parameter.")
End If
If Not String.IsNullOrWhiteSpace(Request.QueryString(qsCaseId)) Then
CaseId = Request.QueryString(qsCaseId).ToString
End If

    If Not String.IsNullOrEmpty(ssrsReportServerUrl) AndAlso Not String.IsNullOrEmpty(caseHistoryFolderPath) Then
        Dim reportViewer As New ReportViewer
        reportViewer.ProcessingMode = ProcessingMode.Remote

        'Assign the reportserver url And path
        reportViewer.ServerReport.ReportServerUrl = New Uri(ssrsReportServerUrl)
        reportViewer.ServerReport.ReportPath = caseHistoryFolderPath

        'Assign the input parameters to report.--add multiple parameters below if you have multiple..i have only one parameter to pass.
        'to show the input parameter textbox entry on the screen , set below property to true.
        Dim rptParameters As New Microsoft.Reporting.WebForms.ReportParameter
        Dim paramarr(0) As Microsoft.Reporting.WebForms.ReportParameter
        rptParameters = New Microsoft.Reporting.WebForms.ReportParameter("CaseID", CaseId, False)
        paramarr(0) = rptParameters
        reportViewer.ServerReport.SetParameters(paramarr)

        '//Set the report properties (width, zoom, refresh, print controls)
        reportViewer.SizeToReportContent = True
        reportViewer.ZoomMode = ZoomMode.FullPage
        reportViewer.AsyncRendering = False
        reportViewer.ShowBackButton = False
        reportViewer.ShowRefreshButton = True
        reportViewer.ShowFindControls = True
        reportViewer.ShowPageNavigationControls = True
        reportViewer.ShowPrintButton = True

        reportViewer.ShowZoomControl = True

        reportViewer.ServerReport.Refresh()
        ViewBag.ReportViewer = reportViewer
    Else
        Throw New ArgumentNullException("Report Server URL or the Report Path is Invalid.")
    End If

    Return View(VIEW_FILE, ViewModel)
End Function

  1. Include the below code snippet in the view section of your page. I'm using vbhtml page . I'm getting the reportviewer viewbag and displaying in the vbhtml page for display.

@Imports ReportViewerForMvc

@Code ViewData("Title") = "Details"

End Code @If Not ViewBag.ReportViewer Is Nothing Then @

@Html.ReportViewer(TryCast(ViewBag.ReportViewer, Microsoft.Reporting.WebForms.ReportViewer), New With {.htmlAttributes = New With {.width = "100%", .height = "100%", .scrolling = "no"}})

End If

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDismissileView Question on Stackoverflow
Solution 1 - C#Mike HildnerView Answer on Stackoverflow
Solution 2 - C#Brant BobbyView Answer on Stackoverflow
Solution 3 - C#KyleMitView Answer on Stackoverflow
Solution 4 - C#Hayu RahizaView Answer on Stackoverflow
Solution 5 - C#LeonView Answer on Stackoverflow
Solution 6 - C#JohnclView Answer on Stackoverflow
Solution 7 - C#Vikas LalwaniView Answer on Stackoverflow
Solution 8 - C#niki bView Answer on Stackoverflow
Solution 9 - C#KrishnaView Answer on Stackoverflow