ASP.NET MVC+WebForms站点Crystal Reports打印提示内存不足求永久解决方案
问题描述
我的网站采用ASP.NET MVC + WebForms C# 结合Crystal Reports开发,点击按钮使用Crystal Report打印报表时,会抛出如下错误:
"Not enough memory resources are available to process this command"
该错误每两天就会出现,目前只能通过重新发布站点临时解决,希望找到永久修复方案。
相关代码
protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { TXTORDERID.Text = Request.QueryString["order_number"].ToString(); TXTDEPTID.Text = Request.QueryString["deptid"].ToString(); TXTTESTID.Text = Request.QueryString["testid"].ToString(); TXTCULTURE.Text = Request.QueryString["Culture"].ToString(); ParameterFields paramFields = new ParameterFields(); ParameterField paramField = new ParameterField(); ParameterDiscreteValue paramDiscreteValue = new ParameterDiscreteValue(); ParameterDiscreteValue paramDiscreteValue1 = new ParameterDiscreteValue(); ParameterField parameterField1 = new ParameterField(); ParameterDiscreteValue parameterDiscreteValue1 = new ParameterDiscreteValue(); ParameterFields parameterFields = new ParameterFields(); if (Convert.ToInt32(TXTDEPTID.Text) == 7 && Convert.ToInt32(TXTCULTURE.Text) == 1) { TXTPOSITIVE.Text = Request.QueryString["Positive"].ToString(); } if (Session["UserCustid"] != null && Convert.ToInt32(Session["UserCustid"]) > 0) { if (Convert.ToInt32(TXTDEPTID.Text) == 1 || Convert.ToInt32(TXTDEPTID.Text) == 2 || Convert.ToInt32(TXTDEPTID.Text) == 3 || Convert.ToInt32(TXTDEPTID.Text) == 4) { paramField.Name = "@ORDER_ID"; paramDiscreteValue.Value = TXTORDERID.Text.ToString(); paramField.CurrentValues.Add(paramDiscreteValue); paramFields.Add(paramField); paramField = new ParameterField(); // <-- This line is added paramDiscreteValue = new ParameterDiscreteValue(); // <-- This line is added paramField.Name = "@branch_id"; paramDiscreteValue1.Value = TXTDEPTID.Text.ToString(); paramField.CurrentValues.Add(paramDiscreteValue1); paramFields.Add(paramField); CrystalReportViewer1.ParameterFieldInfo = paramFields; CrystalReportViewer1.ReuseParameterValuesOnRefresh = true; CrystalReportViewer1.ToolPanelView = CrystalDecisions.Web.ToolPanelViewType.None; reportDocument.Load(Server.MapPath("~/RPT/RPTCREDITRESULTS.rpt")); CrystalReportViewer1.ReportSource = reportDocument; reportDocument.SetDatabaseLogon("admin", "1234"); var connectionInfo = new ConnectionInfo(); connectionInfo.ServerName = "desktop"; connectionInfo.DatabaseName = "DBA"; connectionInfo.Password = "1234"; connectionInfo.UserID = "admin"; connectionInfo.Type = ConnectionInfoType.SQL; connectionInfo.IntegratedSecurity = false; for (int i = 0; i < CrystalReportViewer1.LogOnInfo.Count; i++) { CrystalReportViewer1.LogOnInfo[i].ConnectionInfo = connectionInfo; } CrystalReportViewer1.Dispose(); } if (Convert.ToInt32(TXTDEPTID.Text) == 0) { parameterField1.Name = "@ORDER_ID"; parameterDiscreteValue1.Value = (object)this.TXTORDERID.Text.ToString(); parameterField1.CurrentValues.Add((ParameterValue)parameterDiscreteValue1); parameterFields.Add(parameterField1); //ParameterField parameterField2 = new ParameterField(); //ParameterDiscreteValue parameterDiscreteValue2 = new ParameterDiscreteValue(); //parameterField2.Name = "@deptid"; //parameterDiscreteValue2.Value = (object)this.TXTDEPTID.Text.ToString(); //parameterField2.CurrentValues.Add((ParameterValue)parameterDiscreteValue2); //parameterFields.Add(parameterField2); this.CrystalReportViewer1.ParameterFieldInfo = parameterFields; this.CrystalReportViewer1.ReuseParameterValuesOnRefresh = true; this.CrystalReportViewer1.ToolPanelView = ToolPanelViewType.None; reportDocument.Load(this.Server.MapPath("~/RPT/RPTCREDITRESULTS.rpt")); this.CrystalReportViewer1.ReportSource = (object)reportDocument; reportDocument.SetDatabaseLogon("admin", "1234"); ConnectionInfo connectionInfo = new ConnectionInfo(); connectionInfo.ServerName = "desktop"; connectionInfo.DatabaseName = "DBA"; connectionInfo.Password = "1234"; connectionInfo.UserID = "admin"; connectionInfo.Type = ConnectionInfoType.SQL; connectionInfo.IntegratedSecurity = false; for (int index = 0; index < this.CrystalReportViewer1.LogOnInfo.Count; ++index) this.CrystalReportViewer1.LogOnInfo[index].ConnectionInfo = connectionInfo; CrystalReportViewer1.Dispose(); } } } }
错误截图

待解决疑问
- 曾尝试关闭并释放报表文档对象,但运行报表时始终报错:object reference not set to an instance an object error,请问
reportDocument.Close();和reportDocument.Dispose();代码应该放在什么位置,才不会触发空引用报错? - 将服务器注册表中的Printjoblimit值修改为1500,该操作是否正确?
解决方案
- 修改PrintJobLimit为1500的操作只能延缓报错时间,不是根本解决方案。该参数控制Crystal Reports允许的最大并行处理任务数,默认值为75,调大后只会拉长内存泄漏积累到上限的周期,最终还是会触发内存不足错误。
- 你当前代码的核心问题是只释放了
CrystalReportViewer对象,没有释放reportDocument对象,Crystal Reports底层大量使用非托管资源,不会被.NET垃圾回收机制自动清理,每次打印都会残留资源,积累到上限就会触发报错。 - 资源释放的正确位置:不能在
Page_Load方法内释放,此时报表还未完成渲染返回给前端,提前释放会直接触发空引用错误。推荐两种释放方式:- 第一种:重写页面的
OnUnload事件,页面生命周期结束、内容已经返回给客户端后统一释放资源,代码示例:
protected override void OnUnload(EventArgs e) { base.OnUnload(e); if (reportDocument != null) { reportDocument.Close(); reportDocument.Dispose(); reportDocument = null; } if (CrystalReportViewer1 != null) { CrystalReportViewer1.Dispose(); } // 主动触发垃圾回收,加速非托管资源释放 GC.Collect(); GC.WaitForPendingFinalizers(); }- 第二种:将
reportDocument改为局部变量,用using语句包裹,执行完逻辑后自动释放,示例如下:
// 对应逻辑分支内的代码 using (ReportDocument reportDocument = new ReportDocument()) { // 原有加载报表、设置参数、绑定数据源、绑定到CrystalReportViewer的逻辑全部放在此处 reportDocument.Load(Server.MapPath("~/RPT/RPTCREDITRESULTS.rpt")); // 其他配置逻辑... CrystalReportViewer1.ReportSource = reportDocument; } - 第一种:重写页面的
- 额外优化建议:
- 尽量简化报表逻辑,移除无用的子报表、冗余字段,查询数据时提前过滤减少单报表返回的数据量,降低单任务内存占用
- 若站点报表访问并发量较高,可单独部署独立的报表服务进程,避免报表内存泄漏影响主站点业务稳定性
内容的提问来源于stack exchange,提问作者Ziad Adnan
相关产品推荐
相关产品推荐

