我们有一个 Asp.Net 页面针对后端的 Oracle 数据库运行 RDLC 本地报告,导出到 Excel 电子表格时速度慢得离谱。我做了一些调查,并确定查询本身不应该受到指责 - 我可以使用 SQL Developer 直接针对 Oracle 运行查询,并在大约 5 秒内将结果导出到 Excel,但当我通过 asp.网页和ReportViewer控件大约需要3分钟才能返回。
有人对为什么这么慢有任何建议吗?该查询返回大约 8000 行,每行大约 30 列,因此它不是一个很小的结果集,但也不是很大。任何有关我们如何优化报告的建议将不胜感激。
我正在使用 Microsoft.ReportViewer.WebForms 版本 10.0.0.0,有谁知道 v11 是否有性能改进?
编辑:尝试了ReportViewer v11,速度没有提高。
如果您的报告中有分组。从 .NET 4 开始,当旧版 CAS 被删除时,本地处理的 RDLC 报告需要花费大量时间来执行动态分组或动态过滤器。有一个与此相关的现有讨论https://social.msdn.microsoft.com/Forums/sqlserver/en-US/6d89e2ce-3528-465f-9740-7e22aa7b7aae/slow-performance-with-dynamic-grouping-and -reportviewer-in-local-mode?forum=sqlreportingservices
我在其中找到的最好的解决方案是,
1.创建一个新的 .NET 3.5 库项目并创建一个对报表进行实际处理的文件。
using Microsoft.Reporting.WebForms;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
//As you would expect, the new assembly WebReportviewer.FullTrustReportviewer
//all it does is just run the report. that's it. here is the code, it should be in a separated project:
namespace WebReportviewer
{
[Serializable]
public class FullTrustReportviewer : MarshalByRefObject
{
private ReportViewer FullTrust;
public FullTrustReportviewer()
{
FullTrust = new ReportViewer();
FullTrust.ShowExportControls = false;
FullTrust.ShowPrintButton = true;
FullTrust.ShowZoomControl = true;
FullTrust.SizeToReportContent = false;
FullTrust.ShowReportBody = true;
FullTrust.ShowDocumentMapButton = false;
FullTrust.ShowFindControls = true;
//FullTrust.LocalReport.SubreportProcessing += LocalReport_SubreportProcessing;
//FullTrust.LocalReport.SetBasePermissionsForSandboxAppDomain(new PermissionSet(PermissionState.Unrestricted));
}
public void Initialize(string DisplayName, string ReportPath, bool Visible, ReportParameter[] reportParam, string reportRenderFormat, string deviceInfo, string repMainContent, List<string[]> repSubContent)
{
FullTrust.LocalReport.DisplayName = DisplayName;
FullTrust.LocalReport.ReportPath = ReportPath;
//FullTrust.Visible = Visible;
//FullTrust.LocalReport.LoadReportDefinition(new StringReader(repMainContent));
FullTrust.LocalReport.SetParameters(reportParam);
repSubContent.ForEach(x =>
{
FullTrust.LocalReport.LoadSubreportDefinition(x[0], new StringReader(x[1]));
});
FullTrust.LocalReport.DataSources.Clear();
}
public byte[] Render(string reportRenderFormat, string deviceInfo)
{
return FullTrust.LocalReport.Render(reportRenderFormat, deviceInfo);
}
public void AddDataSources(string p, DataTable datatable)
{
FullTrust.LocalReport.DataSources.Add(new ReportDataSource(p, datatable));
}
public SubreportProcessingEventHandler SubreportProcessing { get; set; }
public static void LocalReport_SubreportProcessing(object sender, SubreportProcessingEventArgs e)
{
LocalReport lr = (LocalReport)sender;
e.DataSources.Clear();
ReportDataSource rds;
if (e.ReportPath.Contains("DataTable2"))
{
DataTable dt = (DataTable)lr.DataSources["DataTable2"].Value;
DataView dv = new DataView(dt);
dv.RowFilter = string.Format("Id={0}", e.Parameters["Id"].Values[0]);
rds = new ReportDataSource("DataTable2", dv.ToTable());
e.DataSources.Add(rds);
}
}
}
}
2.从现有项目中调用代码
public static byte[] GeneratePBAReport()
{
string l_spName = string.Empty;
string l_reportPath = string.Empty;
var repCol = new List<ReportDataSource>();
var repParCol = new ReportParameter[1];
if (id == "")
{
l_reportPath = HttpContext.Current.Server.MapPath("~\\.rdlc");
l_spName = "";
}
else
{
l_reportPath = HttpContext.Current.Server.MapPath("~\\.rdlc");
l_spName = "";
}
repParCol[0] = new ReportParameter("pID", "");
var ds = new DataSet();
using (var sqlCmd = new SqlCommand(l_spName, new SqlConnection(ConfigurationManager.ConnectionStrings[""].ConnectionString)))
{
sqlCmd.CommandType = CommandType.StoredProcedure;
var sqlParam = new SqlParameter() { Value = "", ParameterName = "" };
sqlCmd.Parameters.Add(sqlParam);
sqlCmd.CommandTimeout = 300;
using (var sqlAdapter = new SqlDataAdapter(sqlCmd))
{
sqlAdapter.Fill(ds);
}
}
var rds = new ReportDataSource();
rds.Name = "";
rds.Value = ds.Tables[0];
//l_report.DataSources.Add(rds);
repCol.Add(rds);
rds = new ReportDataSource();
rds.Name = "";
rds.Value = ds.Tables[1];
//l_report.DataSources.Add(rds);
repCol.Add(rds);
rds = new ReportDataSource();
rds.Name = "";
rds.Value = ds.Tables[2];
//l_report.DataSources.Add(rds);
repCol.Add(rds);
rds = new ReportDataSource();
rds.Name = "";
rds.Value = ds.Tables[3];
//l_report.DataSources.Add(rds);
repCol.Add(rds);
Warning[] warnings;
string[] streamids;
string mimeType;
string encoding;
string extension;
string deviceInfo;
deviceInfo = "<DeviceInfo><SimplePageHeaders>True</SimplePageHeaders></DeviceInfo>";
return NewDomainReport.Render("PDF", deviceInfo, "-" , l_reportPath, true, repCol, string.Empty, new List<string[]>(), repParCol);
}
为了真正快速测试,您可以尝试在 web.config 中添加 CAS,如文章中所述。
在 ASP Net 应用程序中,您可以在 web.config 文件的 system.web 部分中使用
<trust legacyCasModel="true" level="Full"/>
来实现相同的结果。
如果速度显示出显着改善,上述代码将表现相同。上面代码的好处是创建一个单独的AppDomain,而不是影响整个解决方案。
添加
<trust legacyCasModel="true" level="Full"/>
对我来说不是一个选项,因为我在代码中使用 dynamic
类型。
此代码正在运行:
public class CustomReportRenderer
{
public static byte[] RenderReport(string reportPath, string rdlcDSName, DataTable rdlcDt, ReportParameter[] rptParams, string downloadFormat, out string mimeType, out string filenameExtension)
{
var assemblyDir = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath);
AppDomainSetup setup = new AppDomainSetup()
{
ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
LoaderOptimization = LoaderOptimization.MultiDomainHost,
PrivateBinPath = assemblyDir
};
setup.SetCompatibilitySwitches(new[] { "NetFx40_LegacySecurityPolicy" });
AppDomain _casPolicyEnabledDomain = AppDomain.CreateDomain("Full Trust", null, setup);
try
{
FullTrustReportviewer rpt = (FullTrustReportviewer)_casPolicyEnabledDomain.CreateInstanceFromAndUnwrap(typeof(FullTrustReportviewer).Assembly.CodeBase, typeof(FullTrustReportviewer).FullName);
rpt.Initialize(reportPath, rptParams);
var bytes = rpt.Render(rdlcDSName, rdlcDt, downloadFormat, out mimeType, out filenameExtension);
return bytes;
}
finally
{
AppDomain.Unload(_casPolicyEnabledDomain);
}
}
}
[Serializable]
public class FullTrustReportviewer : MarshalByRefObject
{
private ReportViewer FullTrust;
public FullTrustReportviewer()
{
FullTrust = new ReportViewer();
FullTrust.ProcessingMode = Microsoft.Reporting.WebForms.ProcessingMode.Local;
}
public void Initialize(string reportPath, ReportParameter[] rptParams)
{
FullTrust.LocalReport.ReportPath = reportPath;
FullTrust.LocalReport.SetParameters(rptParams);
}
public byte[] Render(string rdlcDSName, DataTable rdlcDt, string downloadFormat, out string mimeType, out string filenameExtension)
{
Warning[] warnings;
string[] streamids;
string encoding;
FullTrust.LocalReport.DataSources.Add(new ReportDataSource(rdlcDSName, rdlcDt));
var bytes = FullTrust.LocalReport.Render(downloadFormat, null, out mimeType, out encoding, out filenameExtension, out streamids, out warnings);
return bytes;
}
}
从aspx或mvc控制器方法调用
RenderReport
方法
var bytes = CustomReportRenderer.RenderReport(rdlcFileFullPath, "ReportsDataSet", ds.Tables[0], rptParams, downloadFormat, out mimeType, out extension);
// Now that you have all the bytes representing the PDF report, buffer it and send it to the client.
Response.Buffer = true;
Response.Clear();
Response.ContentType = mimeType;
Response.AddHeader("content-disposition", "attachment; filename=" + fileName + "." + extension);
Response.BinaryWrite(bytes); // create the file
Response.Flush(); // send it to the client to download
简单的表达式和条件格式可能是罪魁祸首。
我们的一份报告在处理大数据(20,000+ 行)时也存在非常类似的问题。该查询返回数据很快,但生成到屏幕上的速度很慢,导出到 Excel 的速度更慢。使用报表查看器 10 和 12 时出现同样的问题。
令我惊讶的是,删除 tablix 排序、日期格式表达式 和 交替背景行颜色表达式 使此报告的生成速度更快,并且导出到 Excel 的时间从半个多小时缩短到大约一分钟.
对于 Microsoft.ReportViewer.WebForms,asp.net 中的版本=15.0.0.0 c# .net Framework 4.8,我把它放在mi代码中:
System.Security.PermissionSet secSetting =new System.Security.PermissionSet(System.Security.Permissions.PermissionState.Unrestricted); ReportViewer1.LocalReport.SetBasePermissionsForSandboxAppDomain(secSetting);