【问题标题】:Viewing SSRS Reports in an ASP.net MVC Site在 ASP.net MVC 站点中查看 SSRS 报告
【发布时间】:2011-05-07 11:22:46
【问题描述】:

有没有办法将 SQL Server Reporting Services 报表查看器控件放在 ASP.net MVC 视图上?如果不是...实现此目的的最佳方法是什么?

【问题讨论】:

  • 我建议添加另一个带有老式 ReprtViewer 和 WebForms 的网站,并将其指向主网站的子文件夹,甚至使用 IFRAME。这是一个干净的解决方案。

标签: c# asp.net-mvc reportviewer reporting-services


【解决方案1】:

不,不在 MVC 视图中。但是您可以将包含服务器控件的 Web 表单页面与您的 MVC 站点混合在一起。

嗯,刚刚google了“mix asp.net mvc and web forms”来找一些例子,google质疑我是不是人:)

无论如何,这里有一个链接 - http://www.packtpub.com/article/mixing-asp.net-webforms-and-asp.net-mvc - 那里有几个。出于同样的原因,我也在 MVC 站点中执行此操作 - 报告控件。

【讨论】:

  • 谢谢。我想可能是这种情况。当您将 WebForms 页面添加到您的 MVC 应用程序时,是否有任何方法可以创建一个“路由”,让您拥有一个漂亮的 URL,而不是看到类似 www.foobar.com/reports/report.aspx 之类的东西?
  • 是的,有 - 你使用的是什么版本的 asp.net?如果是 4.0,请查看 ScottGu 的博客 - weblogs.asp.net/scottgu/archive/2009/10/13/…。如果不是,我不确定 - 还没有这样做。
【解决方案2】:

不,如果您将 ReportViewer 控件放在 MVC 视图中,它将无法工作,因为它需要 ViewState。您必须创建一个老式 Web 表单并将 ReportViewer 放在那里。

我在一个项目中使用的一个解决方案是创建一个自定义路由处理程序,这样我仍然可以使用 URL 路由。路由处理程序将从 RouteData 集合中获取报告名称等参数,创建我的 Web 表单的实例,并通过公共属性将参数传递给它。 Web 表单将在 Page_Load 中读取这些内容并配置 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;
    }
}

当然,如果您决定使用这种方法,这段代码只是一个起点。我创建的那个也在返回之前做了一些用户认证和参数验证。

更新:看起来如果您使用的是 ASP.NET 4.0,most of this can be done automatically!

【讨论】:

    【解决方案3】:

    在 MVC 中实现一个 SSRS ReportViewer 控件包含两个问题:

    1. 至少,您需要为 ReportViewer 控件添加正确的依赖项、处理程序和配置(无论项目类型如何)。
    2. 更棘手的障碍在于混合使用 WebForms 和 MVC。我们需要一种呈现和路由传入请求的方法,以便它们将由 WebForms 页面、控件和操作处理。

    问题 1 - 配置ReportViewer

    如果您过去在设置 ReportViewer 控件方面做了很多工作,那么这可能已经过时了,您可以跳到第 2 部分。

    1. 添加包/引用 - ReportViewer 控件位于 Microsoft.ReportViewer.WebForms.dll 中。您可以通过从 nuget 添加 Microsoft.ReportViewer.WebForms 包来包含在您的项目中:

    2. Web.config 处理程序 - 根据这篇关于 Web.config Settings for ReportViewerthis SO question 的文章,您需要将以下内容添加到您的 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>
      

      根据this question on duplicate keys,通常最容易删除然后重新添加网络服务器配置

    3. 修复损坏的图像请求 - 带有blank.gif images not loading 的 ReportViewer 中存在一个已知缺陷,因此您可以将以下修复添加到您的 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 - 如果还没有,请确保在您的RouteConfig.cs 中输入allow ScriptResources

      routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
      
    5. 添加 ReportViewerPage.aspx - 添加一个包含 ReportViewer 控件实例的 WebForm 页面。为了工作,该控件需要找到一个ScriptManager 控件并放在&lt;form runat="server" &gt; 内。
      因此,您的新 .aspx 页面应如下所示:

      <%@ 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. Page_Load 上连接 ReportViewer - 假设您已经将 SSRS 报告完全部署到报告服务器,该报告服务器可在如下地址获得:

      http://<i>ReportServerName</i>/Reports/Pages/Report.aspx?ItemPath=<i>%2fCompany%2f<b>ClientReport</b></i>

      那么您在新 WebForm 页面中的代码隐藏应该如下所示:

      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. 查看报告 - 此时您应该能够通过选择 在浏览器中查看Ctrl + Shift + W

    问题 2 - 混合 WebForms 和 MVC

    首先,让我们快速剖析一下这些控件的加载方式和后续更新方式之间的路由差异

    • MVC 路由看起来像这样{controller}/{action}/{id},其中路由引擎会自动找到具有指定名称的ControllerAction,传入的请求将由它处理方法。在任何页面请求中,无论是来自页面加载、表单提交、按钮单击、锚导航还是 ajax 调用,都始终在 url {action} 中指定正在执行的确切方法。

    • WebForms 通过查找物理 .aspx 页面地址路由到代码,然后使用 ViewState 和 PostData 连接并触发该页面/控件上的事件。

      这是illustration of different routing formats in WebForms。这是一个简单的按钮单击事件,它将向父页面提交一个帖子,并根据提交的事件数据在页面内引发相应的事件:

    这对我们可用的解决方案是一个很大的限制。 ReportViewer 控件没有什么特别之处。它只是一组复杂的 UserControl 类,它们通过回发当前地址以及 ViewState 和 Event 信息来响应单击和其他输入事件。因此,在 ReportViewer 的路由和导航中包含的任何假设都需要保留到我们的 MVC 包装器中。

    1. 选项 1 - 为 .aspx 页面添加路由

      从 MVC 4.0+ 开始,您可以使用 URL Routing with WebForms。这通过添加Map<b><i>Page</i></b>Route (注意页面部分)与 MVC 很好地混合,以将路由映射到物理文件。因此,将以下内容添加到您的RouteConfig.cs

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

      当您导航到地址 ~/Reports/reportName 时,报告将运行。这可能会从控制器操作内部调用,可能使用一些用户输入的参数或 web.config 连接字符串。有很多ways to manage state in ASP.NETPass Values to ASP.NET Web Forms Pages。一种选择是将信息存储在 Session 中并像这样在控制器中重定向:

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

      然后,在 .aspx 页面中,您可以从 RouteData 值和会话中的任何设置参数中获取 reportName

      // 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];
      

      优点

      • 大部分路由似乎默认都可以,AJAX控件也可以正常工作,所以可以设置AyncRendering=True

      缺点

      • use an ASP Web Form with a Razor MVC Layout 很难,因此渲染将使用户脱离应用程序的其余部分。
      • 此外,报告值必须作为 URL 的一部分公开或通过会话间接传递(而不是直接水合到对象上)。
    2. 选项 2 - 将 .ascx 嵌套在页面上的 PartialView

      改编自How can I use a ReportViewer control with Razor?,只要继承自System.Web.Mvc.ViewUserControl,就可以在PartialViews中使用.ascx控件。

      创建一个名为 ReportViewerControl.ascx 的新 Web 窗体用户控件,如下所示:

      <%@ 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>
      

      注意:必须设置AsyncRendering="False"EnablePartialRendering="false"

      在后面的代码中,您需要将继承类型从System.Web.UI.UserControl 替换为System.Web.Mvc.ViewUserControl

      Page_Init 上,您需要将Context.Handler 设置为Page,以便正确注册事件。

      所以ReportViewerControl.ascx.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();
              }
          }
      }
      

      为了呈现报告,将以下内容添加到您的控制器视图中:

      @Html.Partial("ReportViewerControl", Model)
      

      然后在 ReportViewerControl.ascx.cs Page_Load 事件中,您可以从 ViewUserControl.Model 属性中检索传入的模型,如下所示:

      ReportSetup setup = (ReportSetup)Model;
      

      优点

      • 可以构建到 master _layout.cshtml 并在常规视图中使用
      • 可以直接传模型

      缺点

    进一步阅读

    【讨论】:

    • @Reddy,如果同时发布两种代码语言,答案会很长,但您只需将其复制并粘贴到 converter.telerik.com/ 即可获得相同代码的 C# 语法。
    • 谢谢凯尔。我明白了
    • @KyleMit 我尝试使用你的路线,但我在我的 mvc 应用程序中得到 404,任何故障排除建议
    • 这只是将它连接到Public Class ReportViewer : Inherits Page,这是我作为名为@9​​87654400@ 的库的一部分。当您生成 aspx 文件时,它应该会为您检测。
    • 如果您正在运行 C#,AutoEventWireup 应该为 true,因为 Handles 关键字在 C# (AFAIK) 中没有等效项
    【解决方案4】:

    现在有一个 MvcReportViewer 助手。我们可以从 NuGet 获取它。

    Project Site on GitHub

    NuGet Package

    【讨论】:

    • 您提供的两个链接都是同一个网址。
    【解决方案5】:

    这有点简单,需要一些修复才能将一些像样的东西传递给 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();
        }
    

    【讨论】:

    • 修复一下是什么意思?
    • 嗨,伙计。我使用您的方法下载基于 URL 的 SSRS 文件。我使用 webclient 默认凭据并尝试向用户返回 PDF 文件。但是用户得到一个非零大小的 PDF 文件,它是空白的。你遇到过同样的问题吗?
    • ActionResult 方法返回 View() 被注释掉。 ??
    【解决方案6】:

    一个简单的解决方案是将 iframe 添加到您的 MVC 视图中,以便从报告服务 Web 服务中打开您想要的报告。 iframe 将与报告服务中的组件一起完全运行。如果要将组件移到 MVC 视图中,也可以动态控制 iframe 中用于 url 的参数(例如使用 ajax)。

    虽然这可行,但您仍需要登录网络报告服务(iframe 将打开一个登录对话框)。对于 IE,这是通过使用您的 Windows 登录凭据“自动”完成的。

    【讨论】:

    • 如果用户查看源代码,如何将absoluteurl隐藏到SSRS报告中?
    【解决方案7】:

    您可以使用 ReportViewerForMvc 在 MVC 中查看报告,方法是使用 Nuget 安装它

    Install-Package Microsoft.Report.Viewer -Version 11.0.0
    
    Install-Package Microsoft.ReportViewer.Runtime.WebForms -Version 12.0.2402.15
    
    Install-Package ReportViewerForMvc
    

    如上所示安装 ReportViewer 和其他所需的 Nuget 包后,在 Visual Studio 项目中添加新的 Report.rdlc

    在上面创建的report.rdlc中添加数据集

    现在,在MVC中创建一个ActionMethod,它将从数据库中查询数据并返回报告

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

    在视图中,您有如下代码

    @using ReportViewerForMvc;
    @{
       ViewBag.Title = "Report Student";
     }
     <br/>
    <br />
    
       @Html.ReportViewer(ViewBag.ReportViewer as Microsoft.Reporting.WebForms.ReportViewer)
    

    就是这样,我们完成了。

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

    【讨论】:

      【解决方案8】:

      以防万一它对任何人有所帮助,我发现这些视频教程很容易理解。
      你只需要忍受第一个视频中可怕的背景音乐。

      SSRS 2019 Report in ASP Net MVC 5

      How To Filter SSRS 2019 Report Using Parameter

      我不得不安装“ReportViewerForMvc14”而不是“ReportViewerForMvc”(在视频中使用),因为它不再可用。包装上的说明说它与原始版本相同,但只是更新为可与 ReportViewer 14.0 一起使用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-02-18
        • 2018-09-18
        • 2010-11-29
        • 1970-01-01
        • 1970-01-01
        • 2017-04-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多