web-dev-qa-db-fra.com

Affichage des rapports SSRS dans un site MVC ASP.net

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?

50
Dismissile

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.

16
Mike Hildner

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 !

14
Brant Bobby

Il existe maintenant un assistant MvcReportViewer. Nous pouvons l'obtenir auprès de NuGet.

Site du projet sur GitHub

Package NuGet

11
Hayu Rahiza

L'implémentation d'un contrôle SSRS ReportViewer dans MVC se compose de deux problèmes:

  1. Au minimum, vous devrez ajouter les bonnes dépendances, les bons gestionnaires et la configuration pour le contrôle ReportViewer (quel que soit le type de projet).
  2. L'obstacle le plus délicat réside dans Mélange de WebForms et MVC . Nous avons besoin d'un moyen de rendre et d'acheminer les demandes entrantes afin qu'elles soient gérées par les pages WebForms, les contrôles et les actions.

Problème 1 - Configuration de 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.

  1. 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:

    Nuget - Microsoft.ReportViewer.WebForms

  2. 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

  3. 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"));
        }
    }
    
  4. IgnoreRoute .axd - Si ce n'est pas déjà fait, assurez-vous de autoriser ScriptResources dans votre RouteConfig.cs:

    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    
  5. 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>
    
  6. 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();
            }
        }
    }
    
  7. Afficher le rapport - À ce stade, vous devriez pouvoir afficher votre rapport seul en sélectionnant Afficher dans le navigateur ou Ctrl + Shift + W

    View in Browser

Problème 2 - Mélange de WebForms et MVC

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:

    ASP.NET WebForms - Postback

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.

  1. 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 :

    • La plupart du routage semble fonctionner par défaut, et les contrôles AJAX fonctionnent correctement, vous pouvez donc définir AyncRendering=True

    Inconvénients :

    • Il est difficile de tiliser un ASP formulaire Web avec une disposition Razor MVC donc le rendu éliminera les utilisateurs du flux du reste de l'application.
    • En outre, les valeurs de rapport doivent être exposées dans le cadre de l'URL ou transmises indirectement via la session (au lieu de s'hydrater directement sur l'objet).
  2. 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" et EnablePartialRendering="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 :

    • Peut se transformer en maître _layout.cshtml et consommer dans les vues régulières
    • Peut passer le modèle directement

    Inconvé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 :

10
KyleMit

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();
    }
4
Leon

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.

3
Johncl