首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >减少ReportViewer的内存占用

问减少ReportViewer的内存占用
EN

Code Review用户
提问于 2014-08-20 17:25:19
回答 1查看 5.5K关注 0票数 6

我们已经实现了一个使用C#和ReportViewer通过LocalReport.Render方法创建PDF的过程。当我们必须处理一个大文件,比如20000多条记录时,进程就会在OutOfMemoryException中失败。我知道这个错误可能有很多原因,但我想知道下面的代码是否正常,或者您是否可以给我一个更好的解决方案。我有大约40个case语句,每个语句调用不同的.rdlc文件,这是基于类型的;由于空间不足,我没有包含所有的case语句。

USP_Select_Batch_Address_To_Sort是在执行存储过程的解决方案中定义的接受三个参数的DataSet (在本例中是批处理、运行和序列#)。

这种方法可行吗?如何改进呢?

代码语言:javascript
复制
public static void GenerateIndividualPDFs(string batch, string run, MainDataSet1 DS, MainDataSet1TableAdapters.USP_Select_Batch_Address_To_SortTableAdapter Ta, BindingSource bs, int intStart = 0, int intEnd = 0, string strLetterType = "")
        {
            reportDataSource.Name = "DataSet1";
            reportDataSource.Value = bs;
            int count;
            SqlDataAdapter adapter;

            var batchinfo = new clsBatchInfo();

            ReportViewer report = new ReportViewer();

            report.LocalReport.DataSources.Add(reportDataSource);


            if (!Directory.Exists(mstrFilePath + "\\" + batch + run))
            {
                DirectoryInfo di = Directory.CreateDirectory(mstrFilePath + "\\" + batch + run);
            }
            var DA = new clsGetBatchSort();

            //this dataAdapter will print the full set
            adapter = new SqlDataAdapter(DA.dsBatch_Sort(batch, run));


            DataSet ds = new DataSet();
            adapter.Fill(ds);
            count = ds.Tables[0].Rows.Count;

            int i = 0;
            int LetterTypeID = 0;

            report.ProcessingMode = ProcessingMode.Local;

  foreach (DataRow dr in ds.Tables[0].Rows)
            {
                i++;
                report.Clear();
                Ta.ClearBeforeFill = true;
                try
                {


                LetterTypeID = Convert.ToInt32(dr[2]);
                switch (LetterTypeID)
                {
                    case 1:                                      
                {

                    Ta.Fill(DS.USP_Select_Batch_Address_To_Sort, Convert.ToDecimal(batch), run,  Convert.ToInt32(dr["MPresortID"]));
                    report.LocalReport.ReportEmbeddedResource = "ReportsApplication1.ABC.MAENG.rdlc";
                    ExportReport(Convert.ToInt32(dr["MPresortID"]), batch, run, report);
                    continue;
                }
                    case 2:
                {
                     Ta.Fill(DS.USP_Select_Batch_Address_To_Sort, Convert.ToDecimal(batch), run,  Convert.ToInt32(dr["MPresortID"]));
                    report.LocalReport.ReportEmbeddedResource = "ReportsApplication1.ABC.MASPA.rdlc";
                    ExportReport(Convert.ToInt32(dr["MPresortID"]), batch, run, report);
                    continue;
                }
 catch (Exception ex)
                {
                    clsEmail.EmailMessage("Error Batch " + batch + run, "clsGenerateLetters " + ex.Message);
                    continue;
                    //MessageBox.Show(ex.Message);
                    //return "";
                }
            }
}

 private static void ExportReport(int PreSortID, string batch, string run, ReportViewer Report)
      {           

          Report.RefreshReport();

          Warning[] warnings;
            string[] streamids;
            string mimeType;
            string encoding;
            string filenameExtension;

            string fmt = "00000000";



            byte[] bytes = Report.LocalReport.Render(
                "PDF", null, out mimeType, out encoding, out filenameExtension,
                out streamids, out warnings);

            string filename = mstrFilePath + "\\" + batch + run + "\\" + PreSortID.ToString(fmt) + ".PDF";
            using (FileStream fs = new FileStream(filename, FileMode.Create))
            {
                fs.Write(bytes, 0, bytes.Length);
                fs.Close();
            }
            Report.LocalReport.ReleaseSandboxAppDomain();
      }
EN

回答 1

Code Review用户

发布于 2014-08-20 19:46:12

我可能无法用内存来解决您的问题,但是我会说您正在从ADO将一个大的DataSet加载到内存中,然后您就不是Dispose-ing DataSet了。这将不会释放与DataSet相关的资源(它不一定会在超出范围后被释放或成为GCed ),这可能是一个促成因素。

同样值得注意的是,DataSet的性能比IEnumerable差。我知道这是有原因的,但我不能指手画脚,我不想通过解释错误的原因来损害你的利益,但你可以看看来问个问题在这两者之间的表现。

您可以在使用DataSet从SQL检索IEnumerable后将其转换为Cast<T>。在此之后,您可以在IEnumerable上调用LINQ理解和运算符。这将更快,而且也会被延迟评估--这意味着您可以在IEnumerable上构建一个查询并从您的方法中返回它,并且它将永远不会被迭代,直到您真正需要访问元素。欢庆!更少的计算能力和更少的内存需求。

这也有一个额外的好处,防止您不得不从一个DataTable复制到另一个(这是足够沉重的)。

另一个提示--不要让这个方法返回一个空值。你的方法现在做的太多了。你应该把它分成多个方法,就像这样。

代码语言:javascript
复制
// Where StronglyTypedRepresentation is a type that holds all of the fields
// you expect to receive back from the stored procedure 
// uspSelectBatchAddressToSortTableAdapter
ReportViewer GeneratePdf(IEnumerable<StronglyTypedRepresentation> data) {}
IEnumerable<StronglyTypedRepresentation> MapSqlResult(DataSet data) {}
void SavePdf(ReportViewer reportViewer) {}

请记住,每个方法(/ type)只应该做一件事情。

因此,概括地说:

  • 将方法拆分成更细粒度的块。
  • 使用IEnumerable将初始数据从ADO转换为Cast<T>。LINQ的理解几乎总是更快(需要引用--我只有轶事证据可以提供)。如果需要,始终可以将需要发送到存储过程的内容转换回datatable (如果需要将所有内容转换为存储过程,那么我建议在数据库端执行此计算,因为这是为执行批处理操作而优化的)
  • 考虑到你的文件可能太大了--如果是这样的话,在读取它们之前,找出一些方法来读取它们或分割它们。
票数 10
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/60611

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档