【发布时间】:2015-05-14 22:51:04
【问题描述】:
我在带有 ASP.NET 的 Visual Studio 2010 中使用 Crystal Reports。
我有一个从数据库中提取数据的 DataSet,它似乎可以很好地加载到报告中。当我在浏览器中查看报告时,所有记录都正确加载。
我必须添加一个参数供用户输入,以便过滤掉报告中的条目。问题是当我向报表添加任何类型的参数时,查看器找不到任何记录。这是之前我用选择向导做任何事情。
我添加了一个仅包含数字 1、2 和 3 的下拉列表参数。在选择专家中没有任何内容,这不会影响任何内容。但无论出于何种原因,当它出现在报告中时,不会选择任何记录。一旦我删除参数,它就会再次起作用。
如果有影响,我将通过字段资源管理器添加参数及其值。
奇怪的是,当我让报表直接从数据库中提取数据而不是先创建 DataSet 时,实现工作正常。我不能使用这个实现的原因是它一直在询问数据库登录信息。即使我预定义了信息。我切换到 DataSet 以避免登录提示。
我认为我的 DataSet 实现一定有问题。相关代码在这里:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using CrystalDecisions.CrystalReports.Engine;
using CrystalDecisions.Shared;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Data;
public partial class reportPage : System.Web.UI.Page
{
private ReportDocument rpt;
private string mapPath;
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack) // If the page is loaded for the first time
{
mapPath = "CrystalReport1.rpt";
ConfigureCrystalReport();
}
}
private void ConfigureCrystalReport() // Creates the instance of the report file and binds it to the viewer on the page
{
myDataSet ds = new myDataSet();
myDataSetTableAdapters.myTableTableAdapter dsTA = new myDataSetTableAdapters.myTableTableAdapter();
dsTA.Fill(ds.myTable);
// Initializing the report file
rpt = new ReportDocument();
string reportPath = Server.MapPath(mapPath);
rpt.Load(reportPath);
DataTable dt = ds.myTable;
Response.Write("<script>alert('Table has "+ dt.Rows.Count.ToString() +" rows');</script>");
rpt.SetDataSource(dt);
// Binding the report file to the report viewer on the page
CrystalReportViewer1.ReportSource = rpt;
CrystalReportViewer1.ToolPanelView = CrystalDecisions.Web.ToolPanelViewType.ParameterPanel;
CrystalReportViewer1.HasToggleGroupTreeButton = false;
}
response.write 会在页面上弹出一个警报,说明正在发送到报告的表中有多少行。它计算正确,所以我知道数据正在发送到报告中,但它没有选择任何数据。
我真的不知道为什么它没有选择任何东西。
编辑:经过一些快速测试,看起来我可以将参数添加到列表中而不会破坏报告。当我将实际参数元素放在页面上时,或者当我将它用作选择专家的一部分时,报告无法加载行。
也就是说,报表只有在不弹出参数提示时才有效。
编辑 2:我尝试更改我的 DataSet 代码以使其实际连接到数据库。我以为我正在使用 DataSets 来避免这种情况……但我在教程视频中看到了它。
代码在这里:
string sConnectionString;
sConnectionString = "Password=mYp@ssw0rd*;User ID=sa;" + "Initial Catalog=databaseName;" + "Data Source=serverName";
SqlConnection conn = new SqlConnection(sConnectionString);
conn.Open();
SqlDataAdapter sDA = new SqlDataAdapter("Select * from myTable", conn);
conn.Close();
myDataSet ds = new myDataSet();
sDA.Fill(ds, "myTable");
// Initializing the report file
rpt = new ReportDocument();
string reportPath = Server.MapPath(mapPath);
rpt.Load(reportPath);
rpt.SetDataSource(ds);
毕竟:什么都没有。如果没有参数起作用,它仍然会从表中加载所有数据。只要我添加参数(到页面,或作为选择专家的一部分),就不会加载任何记录。
编辑 3:我尝试过以编程方式传入参数:
rpt.SetParameterValue("par1", 1);
有趣的是,它确实加载了 DataSet 中的所有数据...直到我向它传递了一个新参数。然后没有加载记录,即使我将参数设置回 1。似乎一旦查看器必须处理一个参数,它就会删除所有记录。
我现在真的不知道该去哪里找。据我所知,这是 rpt.SetDataSource() 或 CrystalReportsViewer1.ReportSource = rpt 的问题。
编辑 4:正如 user4663200 指出的那样,当页面回发时,mapPath 为 null 存在问题。我尝试修复它,但遇到了一个新错误,出现一个弹出窗口并说“正在处理文档,请稍候。”永远。我调查了这个错误,多个消息来源告诉我将加载代码从 Page_Load() 转移到 Page_Init()。
重要的是,当页面回发在用户输入参数时,会以某种方式导致此错误。显然,如果页面不回发,该页面会正常加载。
我尝试在 Page_Init 中实现一个更简单的代码版本,但它不起作用,仍然有无休止的加载弹出窗口。
这是我的代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using CrystalDecisions.CrystalReports.Engine;
using CrystalDecisions.Shared;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Data;
private ReportDocument rpt;
private myDataSet ds;
protected void Page_Init(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
ds = new myDataSet();
myDataSetTableAdapters.myTableTableAdapter dsTA = new myDataSetTableAdapters.myTableTableAdapter();
dsTA.Fill(ds.myTable);
// Initializing the report file
rpt = new ReportDocument();
string reportPath = Server.MapPath("myReport.rpt");
rpt.Load(reportPath);
Session.Add("dataSet", ds);
Session["dataSet"] = ds;
rpt.SetDataSource(ds);
CrystalReportViewer1.ReportSource = rpt;
}
else
{
ds = (myDataSet)Session["Report"];
rpt = new ReportDocument();
string reportPath = Server.MapPath("myReport.rpt");
rpt.Load(reportPath);
rpt.SetDataSource(ds);
CrystalReportViewer1.ReportSource = rpt;
}
}
问题是其他解决方案要求将报告保存在会话中,但要做到这一点,需要将 Sessionstate 设置为“InProc”模式。我不能让这个网站脱离“StateServer”模式。我尝试了一种解决方法,将数据集保存在会话中,然后每次将其绑定到新报告。但这也不起作用。
EDIT 4.5 我刚刚尝试将 sessionState 更改为 InProc,但这仍然不起作用。即使我这样做了,报告也是会话中保存的内容。
EDIT 4.75 我尝试在 Page_Init() 中放置一个断点,它揭示了一些有趣的东西。
我可以看到 Session["Report"];在代码的初始运行期间正确保存。
但在输入参数后,我再次查看了 Page_Init(),当它尝试执行 rpt = (ReportDocument)Session["Report"]; Session["Report"] 中的所有内容都为空。它引发了许多空异常,但这些异常仅在断点调试器中可见,它们似乎在正常运行期间从未出现。我很确定这就是为什么它在回帖后一直加载的原因。
所以现在的问题是,输入参数后回传会导致会话中的所有数据丢失。
EDIT 4.825:您可以忽略之前的编辑。我注释掉了这段代码:
protected void Page_UnLoad(object sender, EventArgs e)
{
try
{
rpt.Close();
rpt.Dispose();
}
catch { }
}
去掉这段代码后,我再次查看了 Session["Report"] 变量,它不再为空。
有趣的是,Page_UnLoad 甚至在进入参数条目之前就被调用了。如果 rpt.Close();正在转储对 rpt 的所有引用,那么就好像 Session 变量像指针一样起作用,而不是存储实际的 rpt 对象。因此当 rpt 为 Disposed() 时,Session["Report"] 似乎只是一个空指针。
所以现在它传递了正确的东西......它仍然有无限的“文档处理”屏幕。
EDIT 5:我终于让它工作了。
原来会话是解决方案,但我找不到任何好的实现。
我的大部分相关代码来自here。但你不能只是复制和粘贴。
这是我的实现
private ReportDocument rpt;
private myDataSet ds;
protected void Page_Init(object sender, EventArgs e)
{
if (!IsPostBack)
{
Session["Report"] = null;
}
if (Session["Report"] == null)
{
ds = new myDataSet();
myDataSetTableAdapters.myTableTableAdapter dsTA = new myDataSetTableAdapters.myTableTableAdapter();
DataTable dt = dsTA.GetData();
rpt = new ReportDocument();
rpt.Load(Server.MapPath("myReport.rpt"));
rpt.SetDataSource(dt);
Session.Add("Report", rpt);
CrystalReportViewer1.ReportSource = rpt;
CrystalReportViewer1.ToolPanelView = CrystalDecisions.Web.ToolPanelViewType.ParameterPanel;
CrystalReportViewer1.HasToggleGroupTreeButton = false;
}
}
protected void Page_Load(object sender, EventArgs e)
{
if (Page.IsPostBack)
{
rpt = (ReportDocument)Session["Report"];
CrystalReportViewer1.ReportSource = rpt;
CrystalReportViewer1.ToolPanelView = CrystalDecisions.Web.ToolPanelViewType.ParameterPanel;
CrystalReportViewer1.HasToggleGroupTreeButton = false;
}
}
protected void butReport_Click(object sender, EventArgs e)
{
if (Session["Report"] == null) // Report is not in session (previously loaded) so load report, set params, view and place in session
{
ds = new myDataSet();
myDataSetTableAdapters.myTableTableAdapter dsTA = new myDataSetTableAdapters.myTableTableAdapter();
DataTable dt = dsTA.GetData();
rpt = new ReportDocument();
rpt.Load(Server.MapPath("myReport.rpt"));
rpt.SetDataSource(dt);
Session.Add("Report", rpt);
CrystalReportViewer1.ReportSource = rpt;
CrystalReportViewer1.ToolPanelView = CrystalDecisions.Web.ToolPanelViewType.ParameterPanel;
CrystalReportViewer1.HasToggleGroupTreeButton = false;
}
else
{
rpt = (ReportDocument)Session["Report"];
CrystalReportViewer1.ReportSource = rpt;
CrystalReportViewer1.ToolPanelView = CrystalDecisions.Web.ToolPanelViewType.ParameterPanel;
CrystalReportViewer1.HasToggleGroupTreeButton = false;
}
}
最大的不同是这一行:DataTable dt = dsTA.GetData(); 在我将整个数据集传递给 rpt 之前。我不知道为什么,但是使用 GetData() 的结果创建一个新的 DataTable 可以让报表保持在会话中。
现在,即使所有回发,我的报告也会保持在会话中。我遇到并解决了其他一些问题,例如当用户离开页面并返回时会话未重置。
【问题讨论】:
-
这是否会以“执行超时”异常结束,或者确实如此,您可能需要等待一段时间才能获得超时,我认为默认值为 15 分钟。
-
为了测试这一点,我已经运行了 20 多分钟。它没有抛出任何异常。
标签: c# asp.net crystal-reports