水晶报表开发部署常见棘手问题
-
报Log4net错误
如图所示
出现这样的错误,请查看C:\Windows\assembly目录下的log4net处理器体系结构
当然不止x86结构了,可能会有amd64之类,在x86结构下,我们只需要设置IIS应用程序池x86处理,如图所示,只需启用32位应用程序。
那么问题来了,如果log4net是基于amd64处理器体系结构,那怎么办?
-
已达到系统管理员配置的最大报表处理作业数限制
出现这样的问题,有两种办法解决
-
办法一(修改注册表)
出现这个问题,通常是水晶报表设置了打印次数限制,初始设置是75,那么出现打印次数这样的错误,我们需要设置注册表
[HKEY_LOCAL_MACHINE\SOFTWARE\SAP BusinessObjects\Crystal Reports for .NET Framework 4.0\Report Application Server\InprocServer]
修改PrintJobLimit为最大数,或者设置为-1[HKEY_LOCAL_MACHINE\SOFTWARE\SAP BusinessObjects\Crystal Reports for .NET Framework 4.0\Report Application Server\Server]
修改PrintJobLimit为最大数,或者设置为-1注意:不同版本的水晶报表,对应的注册表结构不同,需要自己查找注册表的对应位置。
如下图,水晶报表对应的位置
-
办法二(程序中设置创建ReportDocument对象队列)
原理:当报表当前使用次数达到最大值的时候,我们将报表ReportDocument资源全部释放掉,重新创建ReportDocument对象。
public class ReportFactory { protected static Queue reportQueue = new Queue(); protected static int iMaxCount = 75; protected static ReportDocument CreateReport(Type reportClass) { object report = Activator.CreateInstance(reportClass); reportQueue.Enqueue(report); return (ReportDocument)report; } public static ReportDocument GetReport(Type reportClass) { if (reportQueue.Count > iMaxCount) { ((ReportDocument)reportQueue.Dequeue()).Close(); ((ReportDocument)reportQueue.Dequeue()).Dispose(); GC.Collect(); } return CreateReport(reportClass); } } 调用方法 CrystalDecisions.CrystalReports.Engine.ReportDocument crReportDocument = new ReportDocument(); crReportDocument = ReportFactory.GetReport(crReportDocument.GetType());
-
-
多行文本对齐
对于这样的问题,我尝试修改水晶报表内部javascript文件,或者使用代码调整,又或者调整内部脚本,没有一次能成功,尽管显示的时候,像word一样的效果,但是打印出来的结果,还是不会变。最后在google搜索下,这样的问题无法解决,最后对这个产品彻底失望。很奇怪的是,像这样的大型的水晶报表公司,连这个问题都无法解决,真是奇葩了。
-
出现通信错误。将停止打印
这类问题很简单
主要是两个问题引起的
-
报表数据绑定和报表属性配置的问题
-
如果是报表数据绑定
如果不是报表配置的问题,请把相关的数据放入Page_Load(object sender, EventArgs e)
-
如果是报表属性配置,请把配置信息放入
Page_Init(object sender, EventArgs e)
-
-
数据绑定问题
出现这个问题,请把数据存储到session(适用不限于一个页面也可以在整个应用程序中使用)或者ViewState(适用单页面)中,比如这样
If(Session["Report"]==null) { boReportDocument = new ReportDocument(); boReportDocument.Load("reportname.rpt"); Session.Add("Report", boReportDocument); } CrystalReportViewer.ReportSource = Session["Report"];
至于为什么为什么要在HttpSession存储ReportDocument对象,经过我的观察,当页面不断的回发加载报表的时候,同时也不断的在C盘window目录下temp创建临时ReportDocument文件,这样就不断的读写文件,那在高负载的这样的环境,是不是损失了很多性能?那为什么从磁盘加载报表在每次回发时可能是一个非常坏的主意?无疑有以下几个问题:
-
- 高负载加载读取报表,所影响的性能?
- 高负载数据库的重新查询,报表每一页的数据呈现所影响的性能?
- 高负载增加应用服务器和数据库之间的网络流量,所影响的性能?
呈现报表数据的时候,不要把报表页面嵌在iframe或者不同的http端口服务下下,尽管你的程序没有错误,但是弹出安装水晶报表activex控件的时候,这个时候页面会刷新,但刷新的不是当前呈现报表数据的窗口,而是iframe父窗口,这时可能activex控件安装可能就无法映射到当前呈现报表数据的窗口了,而是映射到了父窗口了,所以才会出现通信错误。同样不同http端口,也会出现这样的问题,据我的理解,应该是安装activex控件的时候,没有映射到具体的位置。以上是我对这个问题的理解,有错误,请指正。
5.水晶报表刻度单位
-
水晶报表的最小度量单位其实是缇,1厘米=567缇,这个是他的精度。上移和下移手工拖动有时候动作会太大,可以这样做微调,点钟某个字段或对象,可以看到右边的属性框里有如下属性left,top,width等,可以输入这个值进行精确微调。其实就算换算微调,很难调准确,差距还是很大,必须用实际打印出来的报表,使用尺子去量。
6.不支持的操作。无法在 C++ 堆栈中打开由 JRC 引擎处理的文档
你得到这个错误的原因是文件名,路径无效或CR无法访问文件。如果路径和文件名都是正确无疑的,那么你要确保Network Service用户有读/写的Temp文件夹权限(一般C:\Windows\Temp)。如果还不能解决问题也许你的临时文件夹已满,CR没有清理垃圾。如果是这种情况,那么你需要适当的请求GC释放ReportDocument对象资源。
有的时候,CR行为变幻莫测。当你使用ReportDocument对象调用Dispose()之后,随后GC.Collect可能仍然没有清理Temp文件夹。而且就算Temp文件夹中的.rpt文件没有任何限制,CR仍然也有可能停止执行ReportDocument释放资源请求。
很奇怪,这些通常发生在内部函数或事件处理程序中的ReportDocument对象。但是,如果你声明一个全局的ReportDocument对象,并且调用Dispose方法,这时垃圾很快清理掉了。
释放资源代码样例
public override void Dispose() { try { if (this.CrystalReportViewer1 != null) { this.CrystalReportViewer1.Dispose(); this.CrystalReportViewer1 = null; } if (reportdocument != null) { reportdocument.Close(); reportdocument.Dispose(); GC.ReRegisterForFinalize(reportdocument); } } catch (Exception ex) { } } protected void Page_UnLoad(object sender, EventArgs e) { //建立完页面时,释放报表文档资源 try { if (this.CrystalReportViewer1 != null) { this.CrystalReportViewer1.Dispose(); this.CrystalReportViewer1 = null; } if (reportdocument != null) { reportdocument.Close(); reportdocument.Dispose(); GC.ReRegisterForFinalize(reportdocument); } this.Dispose(); this.ClearChildState(); } catch (Exception ex) { } }