如何在ASP.NET MVC 3 Razor视图中使用ReportViewer控件?

62

我正在尝试在 MVC 3 框架中的 Razor 视图中使用 ReportViewer 控件。 在线文档 中提到了拖放操作。有没有关于如何将其插入视图的建议?

8个回答

81
以下解决方案仅适用于单页报告。有关详细信息,请参阅注释。

ReportViewer是一个服务器控件,因此不能在Razor视图中使用。但是您可以将包含ReportViewer的ASPX视图页面、视图用户控件或传统Web表单添加到应用程序中。

您需要确保已将相关处理程序添加到您的web.config中

如果您使用ASPX视图页面或视图用户控件,则需要将AsyncRendering设置为false才能正确显示报告。

更新:

添加了更多示例代码。请注意,在Global.asax中不需要进行任何有意义的更改。

Web.Config

我的最终结果如下:
<?xml version="1.0"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=152368
  -->

<configuration>
  <appSettings>
    <add key="webpages:Version" value="1.0.0.0"/>
    <add key="ClientValidationEnabled" value="true"/>
    <add key="UnobtrusiveJavaScriptEnabled" value="true"/>
  </appSettings>

  <system.web>
    <compilation debug="true" targetFramework="4.0">
      <assemblies>
        <add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add assembly="System.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add assembly="Microsoft.ReportViewer.WebForms, Version=10.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
        <add assembly="Microsoft.ReportViewer.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
      </assemblies>
    </compilation>

    <authentication mode="Forms">
      <forms loginUrl="~/Account/LogOn" timeout="2880" />
    </authentication>

    <pages>
      <namespaces>
        <add namespace="System.Web.Helpers" />
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Routing" />
        <add namespace="System.Web.WebPages"/>
      </namespaces>
    </pages>
  </system.web>

  <system.webServer>
    <validation validateIntegratedModeConfiguration="false"/>
    <modules runAllManagedModulesForAllRequests="true"/>
    <handlers>
      <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>

  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

控制器

控制器动作非常简单。

额外的,File() 动作将 "TestReport.rdlc" 的输出作为 PDF 文件返回。

using System.Web.Mvc;
using Microsoft.Reporting.WebForms;

...

public class PDFController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    public FileResult File()
    {
        ReportViewer rv = new Microsoft.Reporting.WebForms.ReportViewer();
        rv.ProcessingMode = ProcessingMode.Local;
        rv.LocalReport.ReportPath = Server.MapPath("~/Reports/TestReport.rdlc");
        rv.LocalReport.Refresh();

        byte[] streamBytes = null;
        string mimeType = "";
        string encoding = "";
        string filenameExtension = "";
        string[] streamids = null;
        Warning[] warnings = null;

        streamBytes = rv.LocalReport.Render("PDF", null, out mimeType, out encoding, out filenameExtension, out streamids, out warnings);

        return File(streamBytes, mimeType, "TestReport.pdf");
    }

    public ActionResult ASPXView()
    {
        return View();
    }

    public ActionResult ASPXUserControl()
    {
        return View();
    }
}

ASPXView.apsx

ASPXView如下。

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>
<%@ Register Assembly="Microsoft.ReportViewer.WebForms, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
    Namespace="Microsoft.Reporting.WebForms" TagPrefix="rsweb" %>

<!DOCTYPE html>

<html>
<head runat="server">
    <title>ASPXView</title>
</head>
<body>
    <div>
        <script runat="server">
            private void Page_Load(object sender, System.EventArgs e)
            {
                ReportViewer1.LocalReport.ReportPath = Server.MapPath("~/Reports/TestReport.rdlc");
                ReportViewer1.LocalReport.Refresh();
            }
        </script>
        <form id="Form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server">          
        </asp:ScriptManager>
        <rsweb:reportviewer id="ReportViewer1" runat="server" height="500" width="500" AsyncRendering="false"></rsweb:reportviewer>
        </form>        
    </div>
</body>
</html>

ViewUserControl1.ascx

ASPX用户控件的外观如下:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%@ Register Assembly="Microsoft.ReportViewer.WebForms, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
    Namespace="Microsoft.Reporting.WebForms" TagPrefix="rsweb" %>
<script runat="server">
  private void Page_Load(object sender, System.EventArgs e)
  {
      ReportViewer1.LocalReport.ReportPath = Server.MapPath("~/Reports/TestReport.rdlc");
      ReportViewer1.LocalReport.Refresh();
  }
</script>
<form id="Form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
<rsweb:ReportViewer ID="ReportViewer1" runat="server" AsyncRendering="false"></rsweb:ReportViewer>
</form>

ASPXUserControl.cshtml

Razor视图。需要ViewUserControl1.ascx。
@{
    ViewBag.Title = "ASPXUserControl";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>ASPXUserControl</h2>
@Html.Partial("ViewUserControl1")

参考文献

http://blogs.msdn.com/b/sajoshi/archive/2010/06/16/asp-net-mvc-handling-ssrs-reports-with-reportviewer-part-i.aspx

在Web MVC2中将报表绑定到报表查看器


7
你是否使用了ServerReports测试过?因为它可以为我呈现初始的报告页面,但是当我点击控件中的任何按钮(例如“下一页”)时,什么也没有发生。 - rudimenter
2
我和ServerReports有同样的问题,第一页可以正常渲染,但是任何回调都没有反应。 - Alex
3
@AdrianToman请更新答案以表明它仅适用于第一页,以便未来的读者不必在此浪费时间。 - yoel halb
1
这仅适用于单页报告(即使是本地报告),因为在MVC中,后退机制不起作用。要获得完整的解决方案并启用AsyncRendering =“true”的使用,请参见下面的答案。 - yoel halb
1
@rudimenter,有人已经解决了使用ServerReport引起的postback问题了吗?最好的解决方法是使用iframe吗? - ministrymason
显示剩余9条评论

13

这是一个简单的任务。您可以按照以下步骤操作:

  1. 在您的解决方案中创建一个名为Reports的文件夹。
  2. 添加一个ASP.NET Web表单并将其命名为ReportView.aspx
  3. 创建一个名为ReportData的类,并将其添加到Reports文件夹中。将以下代码添加到该类中。

    public class ReportData  
    {  
        public ReportData()  
        {  
            this.ReportParameters = new List<Parameter>();  
            this.DataParameters = new List<Parameter>();  
        }
    
        public bool IsLocal { get; set; }
        public string ReportName { get; set; }
        public List<Parameter> ReportParameters { get; set; }
        public List<Parameter> DataParameters { get; set; }
    }
    
    public class Parameter  
    {  
        public string ParameterName { get; set; }  
        public string Value { get; set; }  
    }
    
  4. 添加另一个类并命名为ReportBasePage.cs。在这个类中添加以下代码。

  5. public class ReportBasePage : System.Web.UI.Page
    {
        protected ReportData ReportDataObj { get; set; }
    
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            if (HttpContext.Current != null)
                if (HttpContext.Current.Session["ReportData"] != null)
                {
                    ReportDataObj = HttpContext.Current.Session["ReportData"] as ReportData;
                    return;
                }
            ReportDataObj = new ReportData();
            CaptureRouteData(Page.Request);
        }
    
    
        private void CaptureRouteData(HttpRequest request)
        {
            var mode = (request.QueryString["rptmode"] + "").Trim();
            ReportDataObj.IsLocal = mode == "local" ? true : false;
            ReportDataObj.ReportName = request.QueryString["reportname"] + "";
            string dquerystr = request.QueryString["parameters"] + "";
            if (!String.IsNullOrEmpty(dquerystr.Trim()))
            {
                var param1 = dquerystr.Split(',');
                foreach (string pm in param1)
                {
                    var rp = new Parameter();
                    var kd = pm.Split('=');
                    if (kd[0].Substring(0, 2) == "rp")
                    {
                        rp.ParameterName = kd[0].Replace("rp", "");
                        if (kd.Length > 1) rp.Value = kd[1];
                        ReportDataObj.ReportParameters.Add(rp);
                    }
                    else if (kd[0].Substring(0, 2) == "dp")
                    {
                        rp.ParameterName = kd[0].Replace("dp", "");
                        if (kd.Length > 1) rp.Value = kd[1];
                        ReportDataObj.DataParameters.Add(rp);
                    }
                }
            }
        }
    }
    
    ScriptManager 添加到 ReportView.aspx 页面。现在向页面添加一个 报表查看器(Report Viewer)。在报表查看器中设置属性 AsyncRendering="false"。以下是代码。

        <rsweb:ReportViewer ID="ReportViewerRSFReports" runat="server" AsyncRendering="false"
            Width="1271px" Height="1000px" >
        </rsweb:ReportViewer>
    
  6. ReportView.aspx.cs中添加两个命名空间(NameSpace)

  7. using Microsoft.Reporting.WebForms;
    using System.IO;
    
    更改 System.Web.UI.PageReportBasePage。只需使用以下代码替换您的代码即可。
    public partial class ReportView : ReportBasePage
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                RenderReportModels(this.ReportDataObj);
            }
        }
    
        private void RenderReportModels(ReportData reportData)
        {
            // This is the Data Access Layer from which a method is called to fill data to the list.
            RASolarERPData dal = new RASolarERPData();
            List<ClosingInventoryValuation> objClosingInventory = new List<ClosingInventoryValuation>();
    
            // Reset report properties.
            ReportViewerRSFReports.Height = Unit.Parse("100%");
            ReportViewerRSFReports.Width = Unit.Parse("100%");
            ReportViewerRSFReports.CssClass = "table";
    
            // Clear out any previous datasources.
            this.ReportViewerRSFReports.LocalReport.DataSources.Clear();
    
            // Set report mode for local processing.
            ReportViewerRSFReports.ProcessingMode = ProcessingMode.Local;
    
            // Validate report source.
            var rptPath = Server.MapPath(@"./Report/" + reportData.ReportName +".rdlc");
    
            //@"E:\RSFERP_SourceCode\RASolarERP\RASolarERP\Reports\Report\" + reportData.ReportName + ".rdlc";
            //Server.MapPath(@"./Report/ClosingInventory.rdlc");
    
            if (!File.Exists(rptPath))
                return;
    
            // Set report path.
            this.ReportViewerRSFReports.LocalReport.ReportPath = rptPath;
    
            // Set report parameters.
            var rpPms = ReportViewerRSFReports.LocalReport.GetParameters();
            foreach (var rpm in rpPms)
            {
                var p = reportData.ReportParameters.SingleOrDefault(o => o.ParameterName.ToLower() == rpm.Name.ToLower());
                if (p != null)
                {
                    ReportParameter rp = new ReportParameter(rpm.Name, p.Value);
                    ReportViewerRSFReports.LocalReport.SetParameters(rp);
                }
            }
    
            //Set data paramater for report SP execution
            objClosingInventory = dal.ClosingInventoryReport(this.ReportDataObj.DataParameters[0].Value);
    
            // Load the dataSource.
            var dsmems = ReportViewerRSFReports.LocalReport.GetDataSourceNames();
            ReportViewerRSFReports.LocalReport.DataSources.Add(new ReportDataSource(dsmems[0], objClosingInventory));
    
            // Refresh the ReportViewer.
            ReportViewerRSFReports.LocalReport.Refresh();
        }
    }
    
  8. 将一个文件夹添加到报表文件夹中,并将其命名为Report。现在将RDLC报表添加到Reports/Report文件夹中,将其命名为ClosingInventory.rdlc

  9. 现在添加一个控制器并将其命名为ReportController。在控制器中添加以下操作方法。

  10. public ActionResult ReportViewer()
        {                
            ViewData["reportUrl"] = "../Reports/View/local/ClosingInventory/";
    
            return View();
        }
    
  11. 添加一个查看页面,点击ReportViewer控制器。将视图页面命名为ReportViewer.cshtml。将以下代码添加到视图页面中。

  12. @using (Html.BeginForm("Login"))
     { 
           @Html.DropDownList("ddlYearMonthFormat", new SelectList(ViewBag.YearMonthFormat, "YearMonthValue",
     "YearMonthName"), new { @class = "DropDown" })
    
    Stock In Transit: @Html.TextBox("txtStockInTransit", "", new { @class = "LogInTextBox" })
    
    <input type="submit" onclick="return ReportValidationCheck();" name="ShowReport"
                     value="Show Report" />
    
    }
    
  13. 添加一个Iframe。将Iframe的属性设置如下

  14. frameborder="0"  width="1000"; height="1000"; style="overflow:hidden;"
    scrolling="no"
    
  15. 将以下JavaScript添加到查看器中。

  16. function ReportValidationCheck() {
    
        var url = $('#hdUrl').val();
        var yearmonth = $('#ddlYearMonthFormat').val();      
        var stockInTransit = $('#txtStockInTransit').val()
    
        if (stockInTransit == "") {
            stockInTransit = 0;
        }
    
        if (yearmonth == "0") {
            alert("Please Select Month Correctly.");
        }
        else {
    
            //url = url + "dpSpYearMonth=" + yearmonth + ",rpYearMonth=" + yearmonth + ",rpStockInTransit=" + stockInTransit;
    
            url = "../Reports/ReportView.aspx?rptmode=local&reportname=ClosingInventory&parameters=dpSpYearMonth=" + yearmonth + ",rpYearMonth=" + yearmonth + ",rpStockInTransit=" + stockInTransit;
    
            var myframe = document.getElementById("ifrmReportViewer");
            if (myframe !== null) {
                if (myframe.src) {
                    myframe.src = url;
                }
                else if (myframe.contentWindow !== null && myframe.contentWindow.location !== null) {
                    myframe.contentWindow.location = url;
                }
                else { myframe.setAttribute('src', url); }
            }
        }
    
        return false;
    }
    
  17. appSettings部分的Web.config文件中添加以下密钥

  18. add key="UnobtrusiveJavaScriptEnabled" value="true"
    
    system.webhandlers 部分添加以下键:
    add verb="*" path="Reserved.ReportViewerWebControl.axd" type = "Microsoft.Reporting.WebForms.HttpHandler, Microsoft.ReportViewer.WebForms, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
    
  19. 将数据源更改为您自己的。这个解决方案非常简单,我认为每个人都会喜欢它。


51
Lol用于简单任务和400行的回答。 - Andrei Dvoynos
6
还有两个神秘的变量声明,从哪个库中声明的谁也不知道,分别是RASolarERPData dalList<ClosingInventoryValuation> objClosingInventory - ProfK
Dal是数据访问层,从中调用一个方法来填充数据到列表中。objClosingInventory = dal.ClosingInventoryReport(this.ReportDataObj.DataParameters[0].Value); - Md. Nazrul Islam

7

我正在使用ASP.NET MVC3和SSRS 2008,当我试图从远程服务器获取报告时,我无法完全按照@Adrian的指示工作。

最后,我发现我需要更改ViewUserControl1.ascx中的Page_Load方法,使其看起来像这样:

ReportViewer1.ProcessingMode = ProcessingMode.Remote;
ServerReport serverReport = ReportViewer1.ServerReport;
serverReport.ReportServerUrl = new Uri("http://<Server Name>/reportserver");
serverReport.ReportPath = "/My Folder/MyReport";
serverReport.Refresh();

我一直想使用 ProcessingMode.Remote

参考资料:

http://msdn.microsoft.com/zh-cn/library/aa337091.aspx - 报表浏览器


非常感谢!我已经尝试了几个小时让它工作!你真的救了我的一天!再次感谢你! - vmg

7
以下是一个完整的解决方案,可直接在MVC .aspx视图中集成报表查看器控件(以及任何asp.net服务器端控件),适用于具有多个页面的报表(不像Adrian Toman的答案),并且AsyncRendering设置为true(基于Steve Sanderson的“Pro ASP.NET MVC Framework”)。
基本上需要做以下几步:
1. 添加一个带有runat =“server”的表单 2. 添加控件(对于报表查看器控件,有时即使使用AsyncRendering =“True”,也可能有效,但在您的特定情况下请检查) 3. 使用具有runat =“server”的脚本标签添加服务器端脚本 4. 使用下面显示的代码重写Page_Init事件,以启用PostBack和Viewstate的使用
以下是演示:
<form ID="form1" runat="server">
    <rsweb:ReportViewer ID="ReportViewer1" runat="server" />
</form>
<script runat="server">
    protected void Page_Init(object sender, EventArgs e)
    {
        Context.Handler = Page;
    }
    //Other code needed for the report viewer here        
</script>

当然建议完全利用MVC方法,通过在控制器中准备所有所需数据,然后通过ViewModel将其传递给视图。这将允许视图的重复使用!
但是,这仅适用于每次Postback都需要的数据,或者即使只需要初始化时也需要(如果数据不太密集,并且数据也不依赖于PostBack和ViewState值)。
但是,即使数据非常密集,有时也可以将其封装到Lambda表达式中,然后将其传递到视图中以在那里调用。
注:要注意以下几点:
- 这样做会使视图本质上变成一个Web表单,具有所有缺点(即PostBacks和可能被非Asp.NET控件覆盖的可能性) - 覆盖Page_Init的黑客未经记录,随时可能会发生更改

我无法让它工作。报告在回发时仍然没有更新。 - ministrymason
@ministrymason 尝试将 AsyncRendering 设置为 false,另外我只在 MVC 3 上进行了测试,并且请注意它仅适用于 .aspx 视图而不是 razor 视图。 - yoel halb
和 @ministrymason 一样的问题。我使用 @Html.Partial 调用它,它可以正常加载,但是其中的控件都无法工作,例如页面导航、展开和折叠表格行等。 - Drazen Bjelovuk
@Bjelovuk 我无法访问您的代码,即使可以,我也不会说排除故障很容易,但我可以给出一些建议。 1)主视图是Razor视图还是aspx? 2)在不是部分时是否有效? 3)在客户端发出后台代码的回传?尝试导航时在HTTP级别背后发生了什么? 4)视图的基类是什么,渲染时Handler和Page的值是什么? - yoel halb

6

感谢您的发布,这个项目绝对棒极了。我已经将它集成到我的应用程序中,快速简单。完美地发挥作用。 - Mike

1

您可以在不使用 iFrames 或 aspx 页面的情况下,在 MVC 页面上显示 SSRS 报告。

大部分工作在这里讲解:

http://geekswithblogs.net/stun/archive/2010/02/26/executing-reporting-services-web-service-from-asp-net-mvc-using-wcf-add-service-reference.aspx

该链接介绍了如何创建一个Web服务和MVC操作方法,以允许您调用报告服务并将Web服务的结果呈现为Excel文件。通过对示例中的代码进行小修改,您可以将其呈现为HTML。
然后,您只需要使用一个按钮调用一个JavaScript函数,该函数会发出一个AJAX调用到您的MVC操作,该操作返回报告的HTML。当AJAX调用返回HTML时,只需用此HTML替换一个div。
我们使用AngularJS,因此下面的示例是以该格式编写的,但它可以是任何JavaScript函数。
$scope.getReport = function()
{
    $http({
        method: "POST",
        url: "Report/ExportReport",
        data: 
                [
                    { Name: 'DateFrom', Value: $scope.dateFrom },
                    { Name: 'DateTo', Value: $scope.dateTo },
                    { Name: 'LocationsCSV', Value: $scope.locationCSV }
                ]

    })
    .success(function (serverData)
    {
        $("#ReportDiv").html(serverData);
    });

};

而 Action 方法 - 主要取自上述链接...

    [System.Web.Mvc.HttpPost]
    public FileContentResult ExportReport([FromBody]List<ReportParameterModel> parameters)
    {
         byte[] output;
         string extension, mimeType, encoding;
         string reportName = "/Reports/DummyReport";
         ReportService.Warning[] warnings;
         string[] ids;

     ReportExporter.Export(
            "ReportExecutionServiceSoap" 
            new NetworkCredential("username", "password", "domain"),
            reportName,
            parameters.ToArray(),
            ExportFormat.HTML4,
            out output,
            out extension,
            out mimeType,
            out encoding,
            out warnings,
            out ids
        );

        //-------------------------------------------------------------
        // Set HTTP Response Header to show download dialog popup
        //-------------------------------------------------------------
        Response.AddHeader("content-disposition", string.Format("attachment;filename=GeneratedExcelFile{0:yyyyMMdd}.{1}", DateTime.Today, extension));
        return new FileContentResult(output, mimeType);
    }

因此,您可以向 SSRS 报告服务器传递参数,该服务器将返回一个报告,您将其呈现为 HTML。所有内容都显示在一个页面上。这是我能找到的最佳解决方案。

1

文档涉及一个ASP.NET应用程序。
你可以尝试查看我的答案这里
我在回复中附上了一个示例。
另一个ASP.NET MVC3的示例可以在这里找到。


1

你不仅需要使用asp.net页面,还要

如果使用Entity Framework或LinqToSql(如果使用部分类),请将数据移动到单独的项目中,报表设计器无法看到这些类。

将报表移到另一个项目/dll中,VS10存在错误,因此asp.net项目无法在Web应用程序中查看对象数据源。然后从dll流式传输报表到您的mvc项目aspx页面。

这适用于mvc和webform项目。在本地模式下使用sql报表并不是一种愉快的开发体验。如果导出大型报表,请注意您的Web服务器内存。reportviewer/export设计非常糟糕。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接