You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,该操作是否正确?

解决方案
  1. 修改PrintJobLimit为1500的操作只能延缓报错时间,不是根本解决方案。该参数控制Crystal Reports允许的最大并行处理任务数,默认值为75,调大后只会拉长内存泄漏积累到上限的周期,最终还是会触发内存不足错误。
  2. 你当前代码的核心问题是只释放了CrystalReportViewer对象,没有释放reportDocument对象,Crystal Reports底层大量使用非托管资源,不会被.NET垃圾回收机制自动清理,每次打印都会残留资源,积累到上限就会触发报错。
  3. 资源释放的正确位置:不能在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;
    }
    
  4. 额外优化建议:
    • 尽量简化报表逻辑,移除无用的子报表、冗余字段,查询数据时提前过滤减少单报表返回的数据量,降低单任务内存占用
    • 若站点报表访问并发量较高,可单独部署独立的报表服务进程,避免报表内存泄漏影响主站点业务稳定性

内容的提问来源于stack exchange,提问作者Ziad Adnan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 19:45:07