Existe-t-il un moyen de placer un contrôle de la visionneuse de rapports SQL Server Reporting Services sur une vue ASP.net MVC? Sinon ... quelle est la meilleure façon d'y parvenir?
Non, pas dans une vue MVC. Mais vous pouvez avoir des pages de formulaires Web qui ont des contrôles serveur mélangés avec votre site MVC.
Hmm, juste googlé "mélanger asp.net mvc et formulaires web" pour trouver quelques exemples, et Google s'est demandé si je suis humain ou non :)
Quoi qu'il en soit, voici un lien - http://www.packtpub.com/article/mixing-asp.net-webforms-and-asp.net-mvc - il y en a quelques-uns. J'ai également fait cela dans un site MVC pour la même raison - le contrôle de rapport.
Non, le contrôle ReportViewer ne fonctionnera pas si vous le placez dans une vue MVC, car il nécessite ViewState. Vous devrez créer un formulaire Web à l'ancienne et y placer le ReportViewer à la place.
Une solution que j'ai utilisée dans un projet sur lequel j'ai travaillé était de créer un gestionnaire d'itinéraire personnalisé, afin que je puisse toujours utiliser le routage d'URL. Le gestionnaire d'itinéraire prendrait des paramètres tels que le nom du rapport de la collection RouteData, créerait une instance de mon formulaire Web et lui transmettrait les paramètres via les propriétés publiques. Le formulaire Web lirait ces informations dans▶Load et configurerait le contrôle 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;
}
}
Ce code n'est qu'un point de départ si vous décidez d'utiliser cette approche, bien sûr. Celui que j'ai créé a également fait une authentification utilisateur et une validation des paramètres avant de revenir.
Mise à jour : On dirait que si vous utilisez ASP.NET 4.0, la plupart de cela peut être fait automatiquement !
Il existe maintenant un assistant MvcReportViewer. Nous pouvons l'obtenir auprès de NuGet.
L'implémentation d'un contrôle SSRS ReportViewer dans MVC se compose de deux problèmes:
ReportViewer
Si vous avez fait beaucoup de choses dans la configuration des contrôles ReportViewer dans le passé, cela peut être un vieux chapeau et vous pouvez passer à la section 2.
Ajouter un package/une référence - Le contrôle ReportViewer
réside dans le Microsoft.ReportViewer.WebForms.dll
. Vous pouvez inclure dans votre projet en ajoutant Microsoft.ReportViewer.WebForms
paquet de nuget:
Gestionnaires Web.config - Par cet article sur Paramètres Web.config pour ReportViewer , et this SO question vous devrez ajouter ce qui suit à votre 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>
Par cette question sur les clés en double , il est généralement plus facile de supprimer puis de rajouter des configurations de serveur Web
Correction des demandes d'images cassées - il y a un défaut connu dans ReportViewer avec blank.gif
les images ne se chargent pas afin que vous puissiez ajouter le correctif suivant à votre 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"));
}
}
IgnoreRoute .axd - Si ce n'est pas déjà fait, assurez-vous de autoriser ScriptResources dans votre RouteConfig.cs
:
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
Ajouter ReportViewerPage.aspx - Ajoutez une page WebForm qui contiendra une instance du contrôle ReportViewer. Pour fonctionner, ce contrôle doit trouver un contrôle ScriptManager
et être placé à l'intérieur d'un <form runat="server" >
.
Votre nouvelle page .aspx devrait donc ressembler à ceci:
<%@ 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>
Câblez ReportViewer sur Page_Load
- En supposant que vous avez déjà un rapport SSRS entièrement déployé sur un serveur de rapports qui est disponible à une adresse comme celle-ci:
http://ReportServerName/Reports/Pages/Report.aspx?ItemPath=%2fCompany%2fClientReport
Ensuite, votre code-behind dans votre nouvelle page WebForm devrait ressembler à ceci:
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();
}
}
}
Afficher le rapport - À ce stade, vous devriez pouvoir afficher votre rapport seul en sélectionnant Afficher dans le navigateur ou Ctrl + Shift + W
Tout d'abord, décortiquons rapidement les différences de routage entre la façon dont ces contrôles sont chargés et mis à jour par la suite
[~ # ~] mvc [~ # ~] les routes ressembleront à ceci {controller}/{action}/{id}
où le moteur de routage trouvera automatiquement un Controller
et Action
avec le nom spécifié et les demandes entrantes seront traitées par cette méthode. Sur toute demande de page, que ce soit à partir du chargement de la page, de la soumission du formulaire, des clics sur les boutons, de la navigation d'ancrage ou des appels ajax, la méthode exacte en cours d'exécution est toujours spécifiée dans l'url {action}
.
WebForms route vers le code en trouvant l'adresse de page physique .aspx, puis utilise ViewState & PostData pour câbler et déclencher des événements sur cette page/contrôle.
Voici un illustration des différents formats de routage dans WebForms . Et voici un simple événement de clic de bouton qui soumettra un message à la page parent et déclenchera les événements appropriés dans la page en fonction des données d'événement soumises:
Il s'agit d'une contrainte assez importante sur nos solutions disponibles. Rien de spécial sur le contrôle ReportViewer
. Il s'agit simplement d'un ensemble sophistiqué de classes UserControl qui répondent aux clics et aux autres événements d'entrée en affichant l'adresse actuelle ainsi que les informations ViewState et Event. Donc, quelles que soient les hypothèses qui ont été intégrées dans le routage et la navigation de ReportViewer, elles devront persister dans notre wrapper MVC.
Option 1 - Ajouter un itinéraire pour la page .aspx
Depuis MVC 4.0+, vous pouvez utiliser routage d'URL avec WebForms . Cela se mélange bien avec MVC en ajoutant un MapPageRoute
(notez la partie Page ) pour mapper un itinéraire vers un fichier physique. Ajoutez donc ce qui suit à votre RouteConfig.cs
:
routes.MapPageRoute(
routeName: "ReportViewer",
routeUrl: "ReportViewer/{reportName}",
physicalFile: "~/ReportViewerPage.aspx"
);
Le rapport s'exécute lorsque vous accédez à l'adresse ~/Reports/reportName
. Cela sera probablement invoqué depuis l'intérieur d'une action du contrôleur, peut-être avec certains paramètres saisis par l'utilisateur ou des chaînes de connexion web.config. Il existe de nombreuses façons de gérer l'état dans ASP.NET et Transmettre des valeurs aux pages de formulaires Web ASP.NET . Une option serait de cacher les informations dans la session et de rediriger comme ceci dans votre contrôleur:
HttpContext.Session[reportSetup.ReportName] = new ReportSetup() {ReportName = "ClientReport"}; //reportSetup;}
return RedirectToRoute("ReportViewer", new { reportName = reportSetup.ReportName});
Ensuite, à l'intérieur de la page .aspx, et vous pouvez récupérer le reportName
des valeurs RouteData et des paramètres de configuration de la session:
// 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];
Avantages :
AyncRendering=True
Inconvénients :
Option 2 - Nest .ascx
à l'intérieur PartialView
sur votre page
Adapté de Comment puis-je utiliser un contrôle ReportViewer avec Razor? , vous pouvez consommer .ascx
contrôles dans PartialViews tant qu'ils héritent de System.Web.Mvc.ViewUserControl
.
Créez un nouveau contrôle utilisateur Web Forms appelé ReportViewerControl.ascx
qui ressemble à ceci:
<%@ 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>
Remarque : vous devez définir
AsyncRendering="False"
etEnablePartialRendering="false"
Dans le code derrière, vous devrez remplacer le type d'héritage de System.Web.UI.UserControl
à System.Web.Mvc.ViewUserControl
.
Et sur Page_Init
, vous devrez définir Context.Handler
à Page
pour que les événements soient correctement enregistrés.
Alors le ReportViewerControl.ascx.cs
devrait ressembler à ceci:
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();
}
}
}
Afin de rendre le rapport, ajoutez ce qui suit à la vue de votre contrôleur:
@Html.Partial("ReportViewerControl", Model)
Et puis dans l'événement ReportViewerControl.ascx.cs memoriesLoad, vous pouvez récupérer le modèle passé dans le ViewUserControl.Model
propriété comme ceci:
ReportSetup setup = (ReportSetup)Model;
Avantages :
_layout.cshtml
et consommer dans les vues régulièresInconvénients :
AsyncRendering
doit être défini sur false, de sorte que les interactions telles que la pagination et le tri provoquent des actualisations de page entière et sont parfois bancales. Brian Hartman a un blog juste pour ReportViewer et parle de AsyncRendering et tous les bagages qui viennent avec .Lectures complémentaires :
C'est un peu simple et nécessitera un peu de correction pour passer quelque chose de décent à une vue dans 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();
}
Une solution simple consiste à ajouter une iframe à votre vue MVC qui ouvre le rapport souhaité à partir du service Web Reporting Services. L'iframe sera pleinement opérationnel avec les composants des services de reporting. Les paramètres utilisés pour l'URL dans l'iframe peuvent également être contrôlés dynamiquement (par exemple avec ajax) si vous souhaitez déplacer les composants vers votre vue MVC.
Bien que cela fonctionne, vous devrez toujours vous connecter au service de création de rapports Web (l'iframe ouvrira une boîte de dialogue de connexion). Pour IE cela se fait "automagiquement" en utilisant vos identifiants de connexion Windows.