This is not going to be a too nice and well-rounded review so I'd better start with some background.
I work in a software company that is specialized in event management software. An 'event' here means the same as a 'happening', something that people are going to attend to. I should write about this more later, but briefly, event management software is like Facebook's event invititation feature for organizations. If you think it a while, there are a lot of organizations that do events for a living. Every educational institution is one. Second, inside every large organization there is a HR, marketing and/or public relations department whose one job is to manage events. And then there is the whole mass event industry.
With such diverse clientele it is expectable that the same software features are used to manage completely different data, and sometimes similar data is managed with different software features. Business processes* and the language used to describe them vary a lot. And because reporting is all about written expression, it is not surprising that one report does not fit all users. As I already explained in the last post, different labels on the same categories are often enough to justify a custom-tailored report.
In practice, we usually get an Excel report someone had previously produced manually, before they started to use our software, and from now on, they want this same report to be generated automatically. At the same time, my boss wants a high-level reporting/charting tool. Something that outputs nice charts from a function that takes all the necessary parameters. These reports should be suitable to all customers, with some small customizations passed as parameters.
From these requirements a basic solution is to have few reasonably generic default reports and a bunch of custom reports for ad-hoc situations. The interface to open a report from the application should be stable. You don't want to modify the report calling interface for each new report, because this would be maintenance nightmare. This is because applications and reports are usually developed separately, and you can't trust that the application has always access to the the latest report definitions.
As I started to work out reports, our first choice of reporting tool was Telerik Reporting, just because we had a licence. Compared to SSRS 2005, Telerik Reporting is almost on par with features, at least starting from version Q1 2009. Other option I had was to use Microsoft ReportViewer in local mode, and later on I have started to test DevExpress XtraReports. Comments on these will follow later but the rest of this post is specific to Telerik Reporting, version Q2 2009.
Telerik Reporting Basics
In order to use any reporting tool effectively, you need to have a basic undertanding of the report lifecycle. Report definitions are designed, definitions and data are processed, and finally the resulting report is rendered. You will be confused If you don't realize when your code runs in the report lifecycle. For example, creating a report object means creating an instance of a report definition. All parameters passed to a constructor affect only this definition instance, not the runtime processing.
Familiarity with the lifecycle is needed most often when wiring user parameters to queries:
- default values of user parameters are stored in the definition. If the report is processed immediately after it is opened, these default values will become selected values
- selected values of user parameters are only known at the runtime processing
Runtime processing behavior is event driven, with events like NeedDataSource to read user parameters, run a query and bind the results to a report element.
Telerik reports definitions are classes inheriting the Report base class. Reports are opened by creating a new report object and assigning it to the report viewer.
Report report = new MyReport()
this.ReportViewer1.Report = report
Because each report definition is a class, you need to use reflection to avoid messing with the interface between application and reporting. The interface I chose was to pass the report class name in the querystring to a report laucher page. This way reports can be opened from the application by redirecting to an url:
/reportlauncher.aspx?type=typename
Simple report parameters could also be added to the url this way.
Parameters
Much of the generalization effort in reporting involves loading available user parameter values dynamically. Available values, usually business categories, would be shown in dropdownlists in the user parameter area. Their effect is to slice the report by parameter value. Telerik reporting supports both single and multivalue parameters, which is good. To populate parameter lists from a datasource, you use the AvailableValues property of the report definition. I had some trouble in getting my datasource right, I couldn't use a dictionary, but arrays and datatables worked fine. Arrays don't have column names like datatables, but the interface requires you to use "Item" as a ValueMember name. This was helpfully instructed in an error message.
Report parameters don't support localization which is a serious shortcoming with no workaround. This is a problem especially with datetime parameters because users are accustomed to certain date formats. Here in Finland the format dd.MM.yyyy is expected but the only supported format in Telerik Reporting is MM/dd/yyyy.
Complex Charts

If you thought that Excel charts are hard to use, think again. Excel team has probably put multiple man-years of work to optimize the usability of charting. On the other hand, if you try to put every setting of a chart into a Visual Studio property grid, you'll get what Telerik charting looks like. This is not a complaint, it is a fact.
Picture on the right is a chart property grid with only a few items expanded. Navigating the grid and finding interdependencies between settings gets hard pretty fast, and it is a trial and error process.
Designing a chart layoyt so that it looks good and clear with as many datasets as possible is a whole topic in itself, a data visualization problem. Thit being said I found that labels and legends are hard to position sensibly. I had a lot of trouble with ugly overlapping labels. Sometimes it is easier to use ordinary textboxes instead of chart labels. One problem I solved this way was positioning the X-axis header and item labels in a bar chart so that they don't overlap each other.
The Evil Wizard
Don't Use Wizard Code You Don't Understand (The Pragmatic Programmer, Tip 50)
Telerik report wizard instructs using the Microsoft DataSet Generator for your queries. Forget it. MSDataSetGenerator is an evil wizard. Once you have generated the code with it, good luck doing any significant changes!
One thing that bugged me was that the wizard-generated adapter has two methods, Fill and GetData with similar signatures. At first, I didn't find any good use for GetData. You would think that once you fill a dataset you can use a reference to it directly. But it turned out that the reference works only for the first time you fill a dataset, but on on subsequent runs.
The problem was that the default user parameter values didn't return any data and I got the dreaded message There is no or empty series, which means that your dataset is empty. I tried to change user parameters and reload the report with no effect. Only after changing Fill to GetData did the dataset refresh.
On top of this all, the dataset wizard/Query Builder does not even work well if you have standard SQL parameters in your query. I don't think you should use it for anything else than playing around.
N-pages and other layout problems
Why is there N pages of duplicates in the report? The reason is that you have defined a datasource for the entire report, with multiple result rows, and the detail section is processed for each result row separately. Take the report datasource away.
Another thing is that you must be careful with the page width. If your report is too wide, blank pages will be rendered. I remember similar issues with SSRS and was expecting this behavior. Page headers are especially tricky, for some reason having a page header generates easily an empty second page in an otherwise one-page report.
Usually blank areas and extra pages have something to do with positioning, but there is at least one exception. If textboxes are invisible and no design mode text shows in the preview, it is probably a localization issue. Revert language to default and remove extra .resx files if you can't get anything else to work.
The latest layout problem that I had was that the report designer canvas didn't show textbox elements that clearly were in the designer file and were visible in the web preview. These elements were outliers that were far separated from others. I had to remove them from the designer file.
Debugging?
Debugging reports is tricky. You cannot debug report code in Visual Studio preview mode. You need to attach the debugger to a windows application or a web application. Now, try your best to make these applications similar, so that they use the same report constructors and all. This is more difficult than it sounds. I have a generic windows test application copied from Telerik samples, but with most of my web applications I need to pass more parameters to the report. So the test app use different constructors than the production app. More than once I've had the trouble that debugging in the windows app is different from debugging in the webapp.
Samples and Forums to the Rescue
God bless, Telerik has provided working examples that look nice, use most of the features and have source files available that actually work. The last thing is important, since if you can't get the examples work in Visual Studio, there is not much point in taking example from them.
Another essential lifeline is to visit the forums. I have definitely needed the forums in every new report task, and usually they have provided an aswer.
Deploying and upgrading
Reports are developed as class library projects in Visual Studio. They will compile to a dll that can be deployed to the application's bin folder. For example, I have copied my report dll directly to DotNetNuke site's /bin.
Upgrading report projects from one Telerik version to other in Visual Studio has never caused any trouble. Reports compile as before. But upgrading requires also that you redeploy both the reports and the Telerik dlls. You must remember to change the Telerik.ReportViewer version number in the application's configuration file, otherwise your reports won't run. This will cause surprises if you have multiple sites and web.config files to maintain.
In Visual Studio, the only small problem I've had was that Telerik ReportViewer didn't show up in the ASP.NET Toolbox in the first installation.
Final Words, Déjà Vu
So, I finished the reports and clients have been happy so far, which is good. But the biggest problem with Telerik Reporting is that I had to do the exact same hoops described here about four months ago. And promptly forgot them afterwards. Wish I had written this blog entry back then. I probably used the same amount of time debugging the same issues now as I did then. This is a real killer, even if its largely my fault for not taking good notes. The knowlegde required to get your reports both work and look good is just very specific. But once you get the first report right, it is straightforward to add similar reports.
Now, my boss wanted a report suite that is easy for him to extend. And he is a CEO/salesman/developer who does not have time to do research. I don't think he has the patience needed to use Telerik Reporting.
Finally, I'd want to point out that I don't dismiss reporting as a job, quite the contrary. But I'm getting more confident that reporting is too important to leave to operational application developers. First, normalized database schemas are not optimal for reporting. Dimensional data models would do a lot to make reporting fun and productive. Second, data quality is not a priority in most CRUD app projects. Schemas are bad, not all tables are even 1NF, policies with null values vary. And third, there are the problems with embedded reporting I have discussed here and in the previous post.
I would recommend separating reporting from applications at all levels:
- In the IT department, source reports from a data warehouse, not from operational databases.
- As an application developer, make report addresses configurable in your operational application, so that users can choose a report server without having to input parameters twice. Supply default reports for your application as SSRS templates, at least in addition to the embedded reports.
-mika-
* Business process is a term that used to imply how people in different organizational 'tribes' end up doing things in their own way. An evolution-based rationalization would be to say that the origins of business processes are quite random, something like out-of-the-hat management decisions. During the implementation the most harmful initiatives are weeded out, managers publicly (but hopefully not privately) forget their mistakes, and the surviving arrangements are what is left as a business process.


4 comments:
I'm about to embark on Telerik Reporting in DNN. I wondered what you put in the web.config to make this work? If you could answer it would be appreciated. Rick@GolkoConsulting.com.
Hi Rick!
In IIS6 you have to put Telerik ReportViewer in the <httpHandlers> section:
<add path="Telerik.ReportViewer.axd" verb="*" type="Telerik.ReportViewer.WebForms.HttpHandler, Telerik.ReportViewer.WebForms, Version=3.1.9.701, Culture=neutral, PublicKeyToken=a9d7983dfcc261be"/>
As you can see, it is version specific. If you upgrade, you'll have to change web.config.
In IIS7 and above, RportViewer is in the <handlers> section:
<add name="Telerik.ReportViewer.axd_*" path="Telerik.ReportViewer.axd" verb="*" type="Telerik.ReportViewer.WebForms.HttpHandler, Telerik.ReportViewer.WebForms, Version=3.1.9.701, Culture=neutral, PublicKeyToken=a9d7983dfcc261be" preCondition="integratedMode,runtimeVersionv2.0"/>
Hope this helps,
-mika-
Hi Mika!
It seems you're well experienced with Telerik reporting. I hope you can help me out with a bug that has been keeping me awake for several nights now.
I'm trying to fix a web application that i've inherited from a previous developer. The Telerik Reports don't seem to work. Everytime a report is generated, the report comes up but with no data in it. This is true to all the existing reports in the application. I've checked the datasets and they are not empty. I created a test application with different set of reports and they work just fine. Since i'm new with Telerik, I'm not so sure what's wrong with the existing application.
A user even reported that the reports only work the first time around. Once she successfully viewed one, any other reports she will be opening from then on would have no data in them.
I'm not sure if you've encountered this problem, but any help would be very much appreciated.
Thanks!
Hi Aldrie,
First, I would recommend is to ask this in the Telerik Forums:
http://www.telerik.com/community/forums/reporting.aspx
There are few things you'll need to specify:
- Are you using MS Dataset Generator datasets? If so, which method did you use, Fill or GetData?
- Did you verify with a debugger that the dataset is filled in the runtime processing (NeedDataSource event)?
- Did you set the report's datasource in runtime procesisng?
I've had the least trouble when using a NeedDataSource event, running an SqlCommand there to get the data, and setting the report's datasource to this data.
A pseudo-VB example:
Sub Report_NeedDataSource(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles MyBase.NeedDataSource
'Take the Telerik.Reporting.Processing.Report instance
Dim report As Telerik.Reporting.Processing.Report = CType(sender, Telerik.Reporting.Processing.Report)
'Read parameters
Dim param = report.Parameters("param")
'Fetch data
Dim commandText As String = "SELECT... "
Dim conn As SqlConnection = New SqlConnection(ConnectionString)
Dim cmd As New SqlCommand(commandText, conn)
cmd.Parameters.Add("@param").Value = param
Dim adapter As New SqlDataAdapter(cmd)
Dim ds As New DataSet()
adapter.Fill(ds)
'Set datasource
report.DataSource = adapter
End Sub
Post a Comment