I'm working on an MVC4 application that needs to render a remote report from SSRS using the ReportViewer. With help from this forum, I've managed to get the page to render under MVC, but callbacks won't work (loads the initial page). Exporting the report works fine (and gives all pages). When I inspect the page, I noticed the following error after changing pages:
Uncaught Sys.WebForms.PageRequestManagerParserErrorException: Sys.WebForms.PageRequestManagerParserErrorException: The message received from the server could not be parsed.
I found this article on combining MVC and Web Forms, but it appears outdated since there are no more Master layout pages. This is related to but not a duplicate of How can I use a reportviewer control in an asp.net mvc 3 razor view? since that article is only for local reports. I've tried changing AsyncRendering to true and false. When true, it doesn't load at all. Any suggestions would be greatly appreciated.
Update: AsyncRendering behavior appears to have changed between previous versions of Visual Studio.
In the end, I ended up having to ditch both my original answer and the callbacks criteria due to the unacceptable security risks. In my case, I wrote controller code rendering the report as HTML to a byte array and from there to a FileContentResult that MVC was kind enough to render as a static HTML page. Exporting as PDF, Excel, or any other options will eventually be implemented in a similar method by changing the Render parameter from HTML4.0 to whatever is appropriate (PDF,XLS) and the MIME type. This approach works with SQL Server 2008R2 and beyond. I haven't tried it with previous versions of SQL Server.
[OutputCache(Duration = 120, VaryByParam = "id")]
public ActionResult ExportHTML(int id)
{
// we need to add code to check the user's access to the preliminary report.
// Also need to consolidate code between ExportHTML and ExportPDF.
var userid = <userid>;
var password = <password>;
var domain = <domain>;
IReportServerCredentials irsc = new myApp.Models.CustomReportCredentials(userid,
password, domain);
var parametersCollection = new List<ReportParameter>();
parametersCollection.Add(new ReportParameter("Snapshot", id.ToString(), false));
ReportViewer rv = new Microsoft.Reporting.WebForms.ReportViewer();
rv.ProcessingMode = ProcessingMode.Remote;
rv.ServerReport.ReportServerCredentials = irsc;
rv.ServerReport.ReportPath = <reportpath>;
rv.ServerReport.ReportServerUrl = new Uri("http://localhost/ReportServer");
rv.ServerReport.SetParameters(parametersCollection);
rv.ServerReport.Refresh();
byte[] streamBytes = null;
string mimeType = "";
string encoding = "";
string filenameExtension = "";
string[] streamids = null;
Warning[] warnings = null;
streamBytes = rv.ServerReport.Render("HTML4.0", null, out mimeType, out encoding,
out filenameExtension, out stream ids,
out warnings);
var HTMLReport = File(streamBytes, "text/html");
return HTMLReport;
}
I'm still hoping for a better answer but in the meantime, my solution appears to satisfy the criteria. It makes use of the Kendo Web Window (so I suppose you could theoretically write your own using jQuery). I haven't modified it yet to pass parameters, but it's a start. I also haven't secured the redirect action, so it's currently possible for a user to view the source, grab the URL from the jQuery load, go to that address and get the underlying report URL from there. I'm going to look into marking it as a ChildActionOnly or some other means of insuring that the action is only available to my window. I also found that I can render the report to HTML4.0, stuff that in a FileResult and load the content that way as well - but then the report is static HTML.
View:
@(Html.Kendo().Grid(Model)
.Name("IndexGrid")
.Columns(col =>
{
col.Bound(c => c.SchoolYear);
col.Bound(c => c.SubmissionTypeDesc);
col.Bound(c => c.EntityDesc);
col.Bound(c => c.SubmissionDate);
col.Bound(c => c.UserName);
col.Bound(c => c.Certified);
col.Command(c =>
{
c.Custom("Edit")
.Text("View")
.Action("Edit", "Draft");
c.Custom("Preview")
.Click("windowOpen");
c.Custom("Certify")
.Action("Certify", "Draft");
c.Custom("Download")
.Action("DumpExcel", "Draft");
}
).Title("<b>Actions</b>")
.HtmlAttributes(new { style = "width:200px;" });
})
.DataSource(ds => ds.Server()
.Model(model => model.Id(pk => pk.snapshot_id))
)
.Sortable(sort => sort.Enabled(true).SortMode(GridSortMode.MultipleColumn).AllowUnsort(true))
.Reorderable(reorder => reorder.Columns(true))
.Groupable(group => group.Enabled(true))
)
</article>
@(Html.Kendo().Window()
.Name("window") //The name of the window is mandatory. It specifies the "id" attribute of the widget.
.Title("Preliminary Report") //set the title of the window
.LoadContentFrom("Redir", "Reports") //define the Action and Controller name
.Visible(false)
.Iframe(true)
.Resizable()
.Width(750)
.Height(500)
.Scrollable(false)
.Draggable()
.Actions(a =>
{
a.Refresh();
a.Minimize();
a.Maximize();
a.Close();
})
)
<script>
function windowOpen(e) {
e.preventDefault();
var window = $("#window").data("kendoWindow");
window.open();
}
</script>
ReportController snippet:
public ActionResult Redir()
{
return RedirectPermanent("../ASPReports/ReportForm.aspx");
}
ReportForm.aspx:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="~/ASPReports/ReportForm.aspx.cs" Inherits="MyApp.Reports.ReportForm"%>
<%@ Register assembly="Microsoft.ReportViewer.WebForms, Version=11.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91" namespace="Microsoft.Reporting.WebForms" tagprefix="rsweb" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title></title>
</head>
<body>
<form id="reportForm" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<div>
<rsweb:ReportViewer ID="mainReportViewer" runat="server" SizeToReportContent="true">
</rsweb:ReportViewer>
</div>
</form>
</body>
</html>
ReportForm.aspx.cs (code-behind):
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
// credentials - could pull from config
var userid = "";
var password = "";
var domain = "";
IReportServerCredentials irsc = new CustomReportCredentials(userid, password, domain);
mainReportViewer.ServerReport.ReportServerCredentials = irsc;
//mainReportViewer.ServerReport.ReportServerUrl =
// new Uri(ConfigurationManager.AppSettings["ReportServerUrl"]);
mainReportViewer.ServerReport.ReportServerUrl =
new Uri("http://localhost/ReportServer");
mainReportViewer.ServerReport.ReportPath = "Path";
mainReportViewer.ProcessingMode = ProcessingMode.Remote;
mainReportViewer.ShowParameterPrompts = false;
mainReportViewer.ShowRefreshButton = false;
mainReportViewer.ShowWaitControlCancelLink = false;
mainReportViewer.ShowBackButton = false;
mainReportViewer.ShowCredentialPrompts = false;
var parametersCollection = new List<ReportParameter>();
//parametersCollection.Add(new ReportParameter("Snapshot", "##", false));
mainReportViewer.ServerReport.SetParameters(parametersCollection);
mainReportViewer.ServerReport.Refresh();
}
}
Just use IFRAME. Create another web site or virtual directory, create application using Web Forms and then show his report viewer pages inside IFRAME on MVC application. You can set report parameters using query string. I have many times placed the report viewer into different systems using this way.