LocalReport hangs sometimes on render - c#

When generating a RDLC report in a C#/WPF application, sometimes it hangs on render (call of render method doesn't return).
The report data comes from a C# object (which is filled in advance with data from a PostgreSQL database) so this issue is not SQL related.
I wonder:
How can I find out the reason?
Is there any way to enable the user to kill the report generation when it hangs?
Embedding the report generation in a using block (as seen in ReportViewer rendering hangs server after some executions) didn't solve the problem.
using (LocalReport report = new LocalReport())
{
report.ReportPath = #"C:\Path\To\MyReport.rdlc";
// Get the report data from db and fill it into MyReportData object.
MyReportData data = CreateReportData();
ReportDataSource headerDataSource = new ReportDataSource();
headerDataSource.Name = "HeaderDataSet";
headerDataSource.Value = data.Header;
report.DataSources.Add(headerDataSource);
// Add more data sources ...
// No report parameters are being used
report.Refresh();
pdfData = report.Render("PDF"); // here it hangs sometimes
}
As said above, the render-call hangs sometimes. After killing the application and generating exactly the same report again it works.

Related

Printing multiple report using Crystal Report crash applcation

I have a WinForms Application (.net 4.0, x86) with Crystal Reports (13.0.15). I want to print multiple documents using Crystal Reports (without seeing them on Report Viewer - directly on printer).
foreach (var document in documents )
{
ReportDocument report=GenerateReport(document.id);
report.PrintToPrinter(printerSettings, pageSettings, false);
}
Everytime it prints 48 reports and on 49th print I've got exception System.ComponentModel.Win32Exception The handle is invalid Void OnStartPrint(System.Drawing.Printing.PrintDocument, System.Drawing.Printing.PrintEventArgs)
I tried another set of documents but I still have exception on 49 print.
I tried to change PrintJobLimit to over 9000 but it doesn't help.
Next I tried to dispose report after print:
foreach (var document in documents )
{
using (ReportDocument report=GenerateReport(document.id))
{
report.PrintToPrinter(printerSettings, pageSettings, false);
}
}
But now program crashes before print 28th report and I cannot catch exception (program stop working). Only in Event Viewer is information about Application Error (Faulting module name: ntdll.dll).
I tried different things (and combination of them):
report.Close();
report.Dispose();
report=null;
even GC.Collect() after print but it still doesn't work.
Anybody has a solution?
It seems like when report.PrintToPrinter returned, the underlying operation sometimes went on. You need to keep reportDocument alive for a few more time, instead of let it go out of scope and GC collection kick in.
My solution is to put them into a delayed queue and Close them later, something like:
foreach (var document in documents )
{
ReportDocument report = GenerateReport(document.id);
report.PrintToPrinter(printerSettings, pageSettings, false);
Task.Run(async () => {
// keep report alive for extra 60 secs
await Task.Delay(60 * 1000);
report.Close();
report.Dispose();
})
}

asp.net report viewer error basics : operation not allowed

I'm trying to get a handle on the basics of report viewer control in a ASP.net Webforms project with C#. I'm using Adventure Work reports to get a feel for the basics.
I have a report called SalesOrderNumber under Report Parts on my SQL server
I just want to be able to view it at this point
if (!Page.IsPostBack)
{
// Set the processing mode for the ReportViewer to Remote
ReportViewer1.ProcessingMode = ProcessingMode.Remote;
ServerReport serverReport = ReportViewer1.ServerReport;
// Set the report server URL and report path
serverReport.ReportServerUrl =
new Uri("(!removed!");
serverReport.ReportPath =
"/Report Parts/SalesOrderNumber";
// Create the sales order number report parameter
ReportParameter salesOrderNumber = new ReportParameter();
salesOrderNumber.Name = "SalesOrderNumber";
salesOrderNumber.Values.Add("SO50750");
// Set the report parameters for the report
ReportViewer1.ServerReport.SetParameters(
new ReportParameter[] { salesOrderNumber });
I get back
The operation you are attempting on item '/Report Parts/SalesOrderNumber' is not allowed for this item type. (rsWrongItemType)
Assign full path like
"http://ReportServername/ReportFolderName/reportname.rdlc"
Make sure Report servername, ServerPath , ReportMode ,rendermode also
I know it's an old question but still I found - it needs an answer.
As per my view, we should first check report path that we have set.
The SSRS ReportPath setting must specify the full report path.
So if you want to access a report, you should set the below path,
http://YourServerName//ReportServer?/Foldername/ReportName
Note: if you've not created a folder on report server then no need to write FolderName. Directly write ReportName.
I would prefer to see below link if you want to access report server using URL.
Click here
Hope it would be helpful to others who are facing the same issue.

Dynamically set Crystal Report still asks for db login

I am trying to deploy Crystal Reports in my MVC application. To get full use of the Crystal Report Viewer, I have to use a webform, which is working fairly well in my dev environment.
The application will be deployed on the user's servers and connect to their personal dbs. This means I do not have the final connection information when designing the report or the application.
I am able to successfully connect using their entries in the web.config file, load a DataTable with the report information, and pass it to the report. However, the report is still asking for the db credentials. The report is set to connect to my db, so it asks for my credentials and will not proceed without them. However, the final report shows the correct info from their db.
I am not sure if I need to change something in the report, or in the code behind. This is how I am setting the report ReportSource now:
protected void Page_Load(object sender, EventArgs e)
{
string strReportName = System.Web.HttpContext.Current.Session["ReportName"].ToString();
try
{
ReportDocument rd = new ReportDocument();
string strRptPath = Server.MapPath("~/") + "Rpts//" + strReportName;
rd.Load(strRptPath);
SqlParameter[] sqlParams = {};
DataTable testDt = DBHelper.GetTable("rptInvtDuplDesc", sqlParams);
rd.DataSourceConnections.Clear();
rd.SetDataSource(testDt);
CrystalReportViewer1.ReportSource = rd;
}
else
{
Response.Write("<H2>Nothing Found; No Report name found</H2>");
}
}
How do I prevent the report from asking for the original credentials?
EDIT:
If I pass the login to my db like this:
rd.SetDatabaseLogon("username", "password");
I do not get the db login again. The credentials have to be for the db used to create the report, but the displayed results are from the DataTable populated in the method above. If it has the data it needs from the current db, why does it need to connect to the original db?
EDIT2:
I have 2 data sources for this report. One is a table from the db and the other is the result from a stored procedure. I have now learned that is the cause of the extra login.
Have a look at my Blog post here on this.
There are a couple of actions required, but the main one is to create a new TableLogOnInfo instance, and then apply is using ApplyLogOnInfo.
You have to provide credentials for each datasouce in the report.
Ideally, the report will have a single datasource. If this is not possible, you need to provide credentials for each, or data for each.
Something like this works well if you want to provide the data:
rd.Database.Tables[0].SetDataSource(testDt);
rd.Database.Tables[1].SetDataSource(micssys);
Otherwise, something like this will allow the report to access the db directly for each datasource:
rd.SetDatabaseLogon("username","password}");

Application runs slow when DB logging info is applied to each Crystal Reports Sections

Currently I use the following method to assign connection info to all the report sections. But as I have many sections in the report, the report is displayed after almost 10 seconds. Which looks really slow. Is there some other method by which we can set logon information to each CR once and for all when it is installed at client side.
JFYI: All the CRs connect to same DB, with same login credentials. Thank you in advance.
readDiamondBillReport = new RealDiamondBill();
crConnectionInfo.ServerName = db.Connection.DataSource;
crConnectionInfo.DatabaseName = db.Connection.Database;
crConnectionInfo.UserID = "client";
crConnectionInfo.Password = "client";
crConnectionInfo.IntegratedSecurity = false;
CrTables = readDiamondBillReport.Database.Tables;
foreach (CrystalDecisions.CrystalReports.Engine.Table CrTable in CrTables)
{
crtableLogoninfo = CrTable.LogOnInfo;
crtableLogoninfo.ConnectionInfo = crConnectionInfo;
CrTable.ApplyLogOnInfo(crtableLogoninfo);
}
Sections crSections2 = readDiamondBillReport.ReportDefinition.Sections;
// loop through all the sections to find all the report objects
foreach (Section crSection in crSections2)
{
ReportObjects crReportObjects = crSection.ReportObjects;
//loop through all the report objects in there to find all subreports
foreach (ReportObject crReportObject in crReportObjects)
{
if (crReportObject.Kind == ReportObjectKind.SubreportObject)
{
SubreportObject crSubreportObject = (SubreportObject)crReportObject;
//open the subreport object and logon as for the general report
ReportDocument crSubreportDocument = crSubreportObject.OpenSubreport(crSubreportObject.SubreportName);
Tables SubCrTables = crSubreportDocument.Database.Tables;
foreach (CrystalDecisions.CrystalReports.Engine.Table SubCrTable in SubCrTables)
{
crtableLogoninfo = SubCrTable.LogOnInfo;
crtableLogoninfo.ConnectionInfo = crConnectionInfo;
SubCrTable.ApplyLogOnInfo(crtableLogoninfo);
}
}
}
}
readDiamondBillReport.Refresh();
I finally found that, neither applying logon info was the issue nor refreshing the report was. But it was my large picture object which I used for setting a watermark in crystal reports.
I had 10 reports which used this Image as watermark. I removed the watermarked image and now following problems are solved:
Project builds very very fast. Previously it took around 1 min to build, which has now reduced drastically to 8-10 secs.
Any changes to the project, especially to reports gets saved much faster.
I used to get "Not enough storage is available to complete this operation" after one or two builds. I had to restart VS and cross my fingers for each of the build.
Crystal Reports are displayed faster on CrystalReportViewer and also objrpt.PrintToPrinter works 500 times faster.
I hope these points will help fellow programmers.
Although you have answered your own question I will point out an alternative approach that can be used with Crystal Reports. Typically people use the "pull" approach in which the connection is set on the Crystal Report and the data set is "pulled" based upon queries embedded within the report.
However, it is possible to use a "push" approach as well. In this scenario you simply bind your Crystal Report data source to an XSD schema and set the data set on the Crystal Report. In .NET you can easily generate the XSD from your data set so this approach is straight forward. You can therefore bind any sub report to the specific table you desire from the passed data set.
The advantage here is that the data can come from any DBMS (database agnostic) and can be manipulated as necessary before being passed to the report (implement custom security, joins, etc).
The caveat being that you would not implement this approach for reports that have larges amounts of data as .NET data sets can be memory intensive.
However, since your performance issue was related to an image this approach would not have helped.
Every report has a subreports collection.
You can apply login info to the tables of each subreport instead of searching for the subreports in each section.
Here is some code
private void showrep(string repName)
{
rd = new ReportDocument();
rd.Load(pth+"\\"+repName);
LogInInfo();
crv.ReportSource = rd; // crv is the reportviewer
crv.Show();
}
private void LogInInfo()
{
MyApp.Properties.Settings s = new MyApp.Properties.Settings();
TableLogOnInfo linfo = new TableLogOnInfo();
linfo.ConnectionInfo.DatabaseName = s.dbname;
linfo.ConnectionInfo.UserID = s.usr;
linfo.ConnectionInfo.Password = s.pw;
linfo.ConnectionInfo.ServerName = s.svr;
foreach (Table t in rd.Database.Tables)
{
t.ApplyLogOnInfo(linfo);
}
foreach (ReportDocument sr in rd.Subreports)
{
foreach (Table t in sr.Database.Tables )
{
t.ApplyLogOnInfo(linfo);
}
}
}
Hope it helps.

Crystal Reports Programatically sort using two passed in parameters

I am pretty new to Crystal Reports and I am probably not using the latest. We are using VS2010/ASP.NET as our main programming environment but we use the integrated Crystal Reports 2008 designer in VS2008, so I need to switch to 2008 when designing reports. I have been passing in paramaters to reports by defining the Parameter Field in the IDE and then passing them in. We have ASP.NET screens with GridViews that are sortable. The data is displayed on the Crystal Report but I have to match how it is sorted. I looked things up on Google and found this and this article. I can't figure out how to use these code snippets. Our best bet seems to be our "Report Controller" classes which is the only place in C# where the Crystal Reports objects are available. A class looks like the following:
public class CRMOCContactsController : ReportingBase
{
public CRMOCContactsController(DataSet reportData, NameValueCollection reportParams)
{
// Create an instance of the Crystal Report.
this.Report = new Reports.CRMOCContacts();
// Get the data
this.ReportData = reportData;
foreach (string s in reportParams.AllKeys)
{
CRHelper.SetCurrentValuesForParameterField(this.Report.ParameterFields, s, reportParams[s]);
}
}
protected override void SetDataSource()
{
this.Report.Database.Tables["ContactRecord"].SetDataSource(this.ReportData.Tables["ContactRecord"]);
this.Report.Database.Tables["ContactSearchCriteria"].SetDataSource(this.ReportData.Tables["ContactSearchCriteria"]);
this.Report.Database.Tables["SSIFields"].SetDataSource(this.ReportData.Tables["SSIFields"]);
}
Does anyone see how I could use these type of classes to do this? one parameter would be the field to sort and another would be the direction. Thanks. Showing me in code really helps.
Tiago, my browser is locked down and doesn't let me add a comment. The only place there is Crystal code is in autogenerated C# classes that go with each report. For instance, the Reports.CRMOCContacts() class referenced above. That is autogenerated by a tool and can't be modified:
//------------------------------------------------------------------------------
//
// This code was generated by a tool.
// Runtime Version:2.0.50727.3603
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
//
//------------------------------------------------------------------------------
There is the only reference to ReportDocument I see:
public virtual CrystalDecisions.CrystalReports.Engine.ReportDocument CreateReport() {
CRMOCContacts rpt = new CRMOCContacts();
rpt.Site = this.Site;
return rpt;
}
I figured something out. The code in CRMOCContactsController can be Crystal code if I treat this.Report as a ReportDocument and add these libraries:
using CrystalDecisions.Shared;
using CrystalDecisions.ReportSource;
using CrystalDecisions.CrystalReports.Engine;
Then I could do:
FieldDefinition FieldDef = null;;
FieldDef = this.Report.Database.Tables[0].Fields[];
this.Report.DataDefinition.SortFields[0].Field = FieldDef;
this.Report.DataDefinition.SortFields[0].SortDirection = CrystalDecisions.Shared.SortDirection.AscendingOrder;
I just don't know how to pass in the sortField? What format?
ReportDocument objReport = new ReportDocument();
objReport.Load("Your report path");
FieldDefinition FieldDef;
FieldDef = objReport .Database.Tables[0].Fields[sortField];
objReport.DataDefinition.SortFields[0].Field = FieldDef;
objReport.DataDefinition.SortFields[0].SortDirection = CrystalDecisions.Shared.SortDirection.DescendingOrder;
Sam, I couldn't figure out how your helper classes are working. It looks like you are passing a dataset to the ReportDocument, right? If this the case, you can pass to the ReportDocument sorted datatables, and it will keep the order of lines.
Here, I am passing a dataset to my report, created from a stored procedures that receives the order by as a parameter. The dataset comes sorted, so the report shows the information sorted as well.

Categories