Tuesday, June 5, 2007

Crystal Report Viewer in .NET

We needed to use Crystal Reports to generate some reports for our web project. The project is in ASP.NET 1.1/C#, so we needed .NET integration. We also needed Excel format (XLS) export. We settled on Crystal Reports 11 for its advanced reporting functionality, integrated .NET Crystal Reports viewer, and multiple export options.

To view a Crystal Report on a .NET page, all you have to do is use the included Crystal Report Viewer, which is basically a .NET control. You can save your Crystal Report as a .RPT file, include it in your .NET web project, and a proxy class (of type CrystalDecisions.CrystalReports.Engine.ReportClass) is auto-generated. For example, if your file is located at /MyReport.rpt, a proxy class called MyReport is auto-generated in /MyReport.cs. The Crystal Report Viewer takes this proxy class as its ReportDataSource, and takes as its datasource any bindable data structure (DataTable, DataSet, DataReader, etc.) that matches the schema of the report itself. So if you used a certain database view to generate your report, your Crystal Report Viewer datasource must be selected from this same view.

Here is some sample C# code:

protected CrystalDecisions.Web.CrystalReportViewer CrystalReportViewer1;
protected MyReport report = new MyReport();
...
DataTable dt = SelectFromDatabaseView(arguments);
CrystalReportViewer1.SetDataSource(dt);
CrystalReportViewer1.ReportSource = report;
First Problem: Permissions

The code worked great so we were ready to push it to another server (the test server). But when I tested the code on my own machine, it didn't work. Here's the error I got when I tried to set the DataSource:

"Access to the path \"C:\\DOCUME~1\\DWBUTL~1\\ASPNET\\LOCALS~1\\Temp\\temp_66cf7594-54e8-43ef-959c-8b47b98500ac.rpt\" is denied."

Apparently Crystal Reports was trying to write to some temporary directory it didn't have access to. My IIS instance was set up to impersonate, which means IIS runs as a specific user rather than the default Internet Guest Account (IUSR_). Apparently this also means that the ASP.NET Worker Process (ASPNET) is not being used, but rather the impersonated account. The other developer wasn't set up for impersonation, which is why he didn't encounter the problem.

Of course, if I had set up impersonation properly in the first place, I may not have had this problem. (Detailed instructions for setting up permissions for the impersonation account are located at MSDN.) I got lazy and didn't follow the full instructions, because everything was working fine.

Strangely enough though, Crystal Reports doesn't seem to use the same directories that IIS is supposed to use. On the test server, Crystal Reports used the C:\Windows\Temp folder. On my own machine, it used C:\Documents and Settings\\ASPNET\Local Settings\Temp. If anyone knows why it would use one rather than the other, I'd love to know the answer. That information will come in handy when we push to production.

Second Problem: Missing Virtual Directory

After I fixed the first problem, the report displayed properly. But then we had a second problem: all the images were broken. Viewing the location of the images, I noticed they were located on http:///crystalreportviewers115/. Apparently, the Crystal Report Viewer works by creating a new web application in your Default Web Site. This is linked via a virtual directory to C:\Program Files\Business Objects\Common\3.5\crystalreportviewers115. (This path is different in different Crystal Report versions. See this link for the path to your version.) The problem is, this virtual directory is created ONLY in the Default Web Site. Wow. Geniuses. What happens if you create a new web site in IIS and push your .NET code to there? Everything breaks.

The only way around this, it appears, is to copy the virtual directory crystalreportviewers115 from the Default Web Site to whatever web site you need it in. The geniuses at Business Objects didn't give an option which web site to install into.

This might be easy or hard, depending on your situation. If you have IIS 6, you can export the virtual directory configuration to an xml file, and then create a new virtual directory in another web site from the same xml file. If you have IIS 5, you'll probably need to copy things manually. If your virtual directory is simply gone, you'll have to create it from scratch.

Conclusion: This is so messed up that it makes babies cry and turn to stone.

No comments: