我们能够让报表查看器正常工作,并且过去几个月一直在生产中使用它,没有任何问题。
- 我们有一个报告控制器,它列出了指向我们要运行的报告的链接
- 单击其中一个链接将对后端进行 ajax 调用并返回一个部分页面,我们可以在其中填写我们需要的所有参数。
- 填写完参数后,我们将表单提交到'\reports\Name of Report'。
- 回到 Reports 控制器,我们调用 SQL,返回我们的数据,然后调用另一个名为“Full Report”的视图
- “完整报表”视图只有一个水晶报表查看器控件,它会自动获取我们通过 ViewData 传递给它的报表数据,填充报表,呈现它,并将其发送给用户
一切似乎都很好。
更新
我在上面最初列出的步骤中添加了一些代码和说明。我遗漏的关键项目是最终视图背后有一些代码,因此它可以与 Crystal Reports 一起使用。后面的代码很少,但需要。为了使 Crystal Reports 正常工作,您最终会得到以下文件:
- 设计报告的布局文件.rpt
- 一个包含 Crystal Reports Report 控件的 aspx 文件。这是将包含一些代码的文件。
有关如何创建适用于 Crystal Reports 的视图的详细信息:
- 使用 Crystal Reports 设计器创建报表的布局。生成的文件将是一个 .rpt 文件。为了本示例,我们将此文件称为 AllJobsSummaryReportLayout.rpt。
- 在设计您的报告时,对于“数据库字段”,选择一个业务实体或 DTO 来保存从 SQL 返回的结果。
- 顺便说一句,我们的系统中有一些数据传输对象 (DTO),它们仅包含标量值和字符串,这些 DTO 中没有智能。当控制器被调用时,它会调用模型,大多数这些报告的模型返回一个 DTO 列表,然后我们将其传递给要呈现的视图。这些 DTO 不知道如何查询自己、显示自己,它们只包含从 SQL 返回的实际值,然后由其他人呈现。
一旦布局水晶报表文件AllJobsSummaryReportLayout.rpt,我们设计我们的Controller。在控制器中,我们获取运行报告所需的任何参数,调用模型,模型返回我们的 DTO 列表,如下面的控制器片段所示:
var reportViewData = model.AllJobsSummaryQuery(startDate, endDate);
if (0 != reportViewData.Count())
{
var report = new AllJobsSummaryReportLayout();
report.SetDataSource(reportViewData);
report.SetParameterValue("startDate", startDate);
report.SetParameterValue("endDate", endDate);
ViewData["ReportData"] = report;
returnView = "AllJobsSummaryView";
}
else
returnView = "noReportView";
return View(returnView);
请注意这里的几个项目,我们正在创建一个变量“报告”,它是我们在上面创建的 Crystal Report 布局文件 AllJobsSummaryReportLayout.rpt 的一种类型。
一旦我们创建了“报告”变量,我们就设置了数据源值和我们需要的任何参数,并将项目捆绑到 ViewData 中。
现在让我们看一下AllJobsSummaryView.aspx。该文件上有一个带有 Crystal Reports 查看器的表单和一个代码隐藏文件:
<%@ Page Title="All Jobs Summary Report" Language="C#" AutoEventWireup="true" CodeBehind="AllJobsSummaryView.aspx.cs" Inherits="V.Views.Reports.AllJobsSummaryView"%>
<%@ Register Assembly="CrystalDecisions.Web, Version=10.5.3700.0, Culture=neutral, PublicKeyToken=692fbea5521e1304" Namespace="CrystalDecisions.Web" TagPrefix="CR" %>
<form id="form1" runat="server">
<div>
<a href="/Reports" id="Report"><< Return to Report Main
Page</a><br />
<CR:CrystalReportViewer ID="ReportViewer" runat="server" AutoDataBind="True" EnableDatabaseLogonPrompt="False"
EnableParameterPrompt="False" HasCrystalLogo="False" DisplayGroupTree="False"
HasDrillUpButton="False" HasToggleGroupTreeButton="False" HasViewList="False"
HasSearchButton="False" EnableDrillDown="False" EnableViewState="True"
Height="50px" ReportSourceID="CrystalReportSource1" Width="350px" />
<CR:CrystalReportSource ID="CrystalReportSource1" runat="server">
<Report FileName="AllJobsSummaryReportLayout.rpt">
</Report>
</CR:CrystalReportSource>
</div>
</form>
以及文件背后的代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace V.Views.Reports
{
public partial class AllJobsSummaryView : ViewPage
{
protected void Page_Init(object sender, EventArgs e)
{
ReportViewer.ReportSource = ViewData["ReportData"];
}
protected void Page_Unload(object sender, EventArgs e)
{
((AllJobsSummaryReportLayout)ViewData["ReportData"]).Close();
((AllJobsSummaryReportLayout)ViewData["ReportData"]).Dispose();
}
}
}
Page_Unload 是关键,没有它,Crystal Reports 将生成错误“您已超出管理员设置的最大报告数”。
这种方法在生产环境中仍然工作了两年多。