c# - services - Visualización de informes de SSRS en un sitio MVC de ASP.net
reporting services mvc 5 (6)
Ahora hay un ayudante de MvcReportViewer. Podemos obtenerlo de NuGet.
¿Hay alguna manera de poner un control de visor de informes de SQL Server Reporting Services en una vista MVC de ASP.net? Si no ... ¿cuál es la mejor manera de lograr esto?
Esto es un poco simple y requerirá un poco de fijación para pasar algo decente a una vista en 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();
}
Implementar un control ReportViewer de SSRS en MVC consta de dos problemas:
- Como mínimo, deberá agregar las dependencias, los controladores y la configuración correctos para el control ReportViewer (independientemente del tipo de proyecto).
- El obstáculo más complicado está en Mixing WebForms y MVC . Necesitamos una forma de procesar y enrutar las solicitudes entrantes para que sean manejadas por las páginas, controles y acciones de WebForms.
Problema 1 - Configurar el ReportViewer
Si ha hecho mucho con la configuración de los controles ReportViewer en el pasado, esto podría ser viejo y puede saltar a la sección 2.
Agregar paquete / referencia : el control
ReportViewer
vive enMicrosoft.ReportViewer.WebForms.dll
. Puede incluir en su proyecto agregando el paqueteMicrosoft.ReportViewer.WebForms
de nuget:Controladores de Web.config : según este artículo sobre la configuración de Web.config para ReportViewer , y esta pregunta de SO , deberá agregar lo siguiente a su
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>
Según esta pregunta sobre claves duplicadas , normalmente es más fácil de eliminar y luego volver a agregar las configuraciones del servidor web
Repara solicitudes de imágenes rotas : hay un defecto conocido en ReportViewer con imágenes en
blank.gif
no se están cargando, por lo que puede agregar la siguiente solución a suglobal.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")); } }
IgnoreRoute .axd : si aún no está allí, asegúrese de permitir ScriptResources en su
RouteConfig.cs
:routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
Agregar ReportViewerPage.aspx : agregue una página WebForm que contendrá una instancia del control ReportViewer. Para funcionar, ese control necesita encontrar un control
ScriptManager
y colocarse dentro de un<form runat="server" >
.
Por lo tanto, su nueva página .aspx debería verse más o menos así:<%@ 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>
Transmita ReportViewer en
Page_Load
- Suponiendo que ya tiene un informe SSRS completamente implementado en un servidor de informes que está disponible en una dirección como esta:http:// ReportServerName /Reports/Pages/Report.aspx?ItemPath= %2fCompany%2f ClientReport
Entonces, su código subyacente en su nueva página WebForm debería verse así:
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(); } } }
Ver informe : en este punto, debería poder ver su informe por sí mismo seleccionando Ver en el navegador o Ctrl + Mayús + W
Problema 2 - Mezcla de WebForms y MVC
Primero, analicemos rápidamente las diferencias de enrutamiento entre cómo se cargan estos controles y luego se actualizan
Las rutas de MVC se parecerán a este
{controller}/{action}/{id}
donde el motor de enrutamiento encontrará automáticamente unController
yAction
con el nombre especificado y las solicitudes entrantes serán manejadas por ese método. En cualquier solicitud de página, ya sea desde carga de página, envío de formulario, clics de botón, navegación de anclaje o llamadas ajax, el método exacto que se ejecuta siempre se especifica en url{action}
.WebForms enruta el código buscando la dirección física de la página .aspx y luego usa ViewState y PostData para conectar y activar eventos en esa página / control.
Aquí hay una ilustración de diferentes formatos de enrutamiento en WebForms . Y aquí hay un simple evento de clic de botón que enviará una publicación a la página principal y levantará los eventos apropiados dentro de la página según los datos del evento enviados:
Esta es una restricción bastante grande en nuestras soluciones disponibles. Nada es especial sobre el control ReportViewer
. Es solo un conjunto sofisticado de clases UserControl que responde al clic y a otros eventos de entrada publicando la dirección actual junto con el ViewState y la información del evento. Por lo tanto, cualquiera que sea la suposición incluida en el enrutamiento y la navegación del ReportViewer deberá persistir en nuestro contenedor MVC.
Opción 1 - Agregar ruta para la página .aspx
A partir de MVC 4.0+, puede usar weblogs.asp.net/scottgu/archive/2009/10/13/… . Esto se combina bien con MVC agregando una MapPageRoute (observe la parte de página ) para asignar una ruta a un archivo físico. Añada lo siguiente a su
RouteConfig.cs
:routes.MapPageRoute( routeName: "ReportViewer", routeUrl: "ReportViewer/{reportName}", physicalFile: "~/ReportViewerPage.aspx" );
El informe se ejecutará cuando navegue a la dirección
~/Reports/reportName
. Probablemente se invocará desde dentro de una acción del controlador, quizás con algunos parámetros ingresados por el usuario o cadenas de conexión web.config. Hay muchas formas de administrar el estado en ASP.NET y Pasar valores a páginas de formularios web ASP.NET . Una opción sería esconder la información en la sesión y redirigir de esta manera en su controlador:HttpContext.Session[reportSetup.ReportName] = new ReportSetup() {ReportName = "ClientReport"}; //reportSetup;} return RedirectToRoute("ReportViewer", new { reportName = reportSetup.ReportName});
Luego, dentro de la página .aspx, y puede obtener el
reportName
de los valores de RouteData y los parámetros de configuración de la sesión:// 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 :
- La mayoría del enrutamiento parece funcionar por defecto, y los controles AJAX funcionan bien, por lo que puede establecer
AyncRendering=True
Contras :
- Es difícil utilizar un formulario web ASP con un diseño Razor MVC, por lo que el renderizado sacará a los usuarios del flujo del resto de la aplicación.
- Además, los valores del informe deben exponerse como parte de la URL o pasarse indirectamente a través de la sesión (en lugar de hidratarse directamente sobre el objeto).
- La mayoría del enrutamiento parece funcionar por defecto, y los controles AJAX funcionan bien, por lo que puede establecer
Opción 2: Nest
.ascx
dentro dePartialView
en tu páginaAdaptado de ¿Cómo puedo usar un control ReportViewer con Razor? , puede consumir controles
.ascx
en PartialViews siempre que hereden deSystem.Web.Mvc.ViewUserControl
.Cree un nuevo control de usuario de formularios web llamado
ReportViewerControl.ascx
que se ve así:<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="ReportViewerControl.ascx.cs" Inherits="MVCAppWithReportViewer.ReportViewerControl" %> <%@ Register TagPrefix="rsweb" Namespace="Microsoft.Reporting.WebForms" Assembly="Microsoft.ReportViewer.WebForms, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" %> <form id="form1" runat="server"> <rsweb:ReportViewer ID="ReportViewer" runat="server" Height="100%" Width="100%" SizeToReportContent="True" ProcessingMode="Remote" AsyncRendering="False" /> <asp:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="false" /> </form>
Nota : debe establecer
AsyncRendering="False"
yEnablePartialRendering="false"
En el código subyacente, deberá reemplazar el tipo de herencia de
System.Web.UI.UserControl
aSystem.Web.Mvc.ViewUserControl
.Y en
Page_Init
, tendrá que configurarContext.Handler
a laPage
para que los eventos se registren correctamente.Entonces,
ReportViewerControl.ascx.cs
debería verse así: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(); } } }
Para presentar el informe, agregue lo siguiente a su vista de controlador:
@Html.Partial("ReportViewerControl", Model)
Y luego en el evento ReportViewerControl.ascx.cs Page_Load, puede recuperar el modelo pasado en la propiedad
ViewUserControl.Model
esta manera:ReportSetup setup = (ReportSetup)Model;
Pros :
- Se puede construir en master
_layout.cshtml
y consumir en vistas regulares - Puede pasar el modelo directamente
Contras :
-
AsyncRendering
debe establecerse en falso, por lo que las interacciones como la paginación y la clasificación causan actualizaciones completas de la página y, a veces, son poco claras. Brian Hartman''s tiene un blog solo para ReportViewer y habla sobre AsyncRendering y todo el equipaje que viene con él .
- Se puede construir en master
Lectura adicional :
- ¿Cómo puedo usar un control reportviewer en una vista de navaja ASP.NET MVC 3?
- ¿Cómo presento una página aspx de ReportViewer remota en MVC 4?
- MVC 5 y SSRS ReportViewer - ¿Cómo implementar?
No, el control ReportViewer no funcionará si lo coloca en una vista MVC, ya que requiere ViewState. Deberá crear un formulario web antiguo y colocar el ReportViewer en su lugar.
Una solución que utilicé en un proyecto en el que trabajé fue crear un controlador de ruta personalizado, por lo que todavía podía hacer uso del enrutamiento de URL. El manejador de ruta tomaría parámetros como el nombre del informe de la colección RouteData, crearía una instancia de mi formulario web y le pasaría los parámetros a través de propiedades públicas. El formulario web los leería en Page_Load y configuraría el control ReportViewer.
// 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;
}
}
Este código es solo un punto de partida si decides usar este enfoque, por supuesto. El que creé también realizó una autenticación de usuario y validación de parámetros antes de volver.
Actualización : parece que si está utilizando ASP.NET 4.0, ¡la weblogs.asp.net/scottgu/archive/2009/10/13/… !
No, no en una vista de MVC. Pero puede tener páginas de formularios web que tengan controles de servidor mezclados con su sitio MVC.
Hmm, simplemente busqué en Google "mix asp.net mvc y formularios web" para encontrar algunos ejemplos, y google me preguntó si soy humano o no :)
De todos modos, aquí hay un enlace - http://www.packtpub.com/article/mixing-asp.net-webforms-and-asp.net-mvc - hay algunos por ahí. También hice esto en un sitio de MVC por el mismo motivo: el control de informes.
Una solución simple es agregar un marco flotante a su vista de MVC que abre el informe que desea del servicio web de servicios de informes. El iframe estará en pleno funcionamiento con los componentes de los servicios de informes. Los parámetros utilizados para la url en el iframe también se pueden controlar dinámicamente (por ejemplo, con ajax) si desea mover los componentes a su vista MVC.
Aunque esto funciona, igual tendrá que iniciar sesión en el servicio de informes web (el iframe abrirá un diálogo de inicio de sesión). Para IE, esto se hace "automágicamente" usando sus credenciales de inicio de sesión de Windows.