IQueryable Methods on ActiveReports ControlCollections
Posted by David on July 25, 2008
I’m currently working on a project with an extremely complex, multi-page report. Unfortunately, a customer requirement was EXACT typography to the “Excel” report and there are hundreds (literally) of data points randomly on the page.
There isn’t a good way to iterate through the results (it’s a very detailed grade card for elementary students) and still match the layout requirements.
So, I went about “drawing” it out—lots of Label controls, lines, boxes, and such. To save myself a bit of time assigning DataFields, querying results, etc., I opted for a different kind of iteration—control iteration.
My schema was simple: q#i#, for the quarter and the primary data point of the report, the indicator Id. Since my business object, a Report, was already assigned to the ActiveReport document, I could simply iterate away!
Unfortunately, if you try to determine if the detail.Controls (a ControlCollection) contains a control and it doesn’t exist—it doesn’t simply return null, it throws an exception. In addition, the ControlCollection doesn’t have an Exists or a FindControl method. So, you’re stuck catching exceptions.
foreach (var indicator in _indicators)
{
try
{
var indicatorHeader =
string.Format(“i{0}”, indicator.Id);
((Label)detail.Controls[indicatorHeader]).Text =
IsSpanish
? indicator.SpanishText : indicator.EnglishText;
}
catch (Exception)
{
continue;
}
}
Unfortunately, that destroys performance—especially with hundreds of iterations (of indicators).
There’s a way around this, at least in my opinion. Instead of addressing the controls as part of the details.Controls ControlCollection, use a custom IQueryable collection.
Begin by adding a private variable to the report to hold the controls. In my case, all of the controls I’ll be modifying are Label (of ARControl) controls.
private IQueryable<Label> _controls;
On my ReportStart (I’m sure I could use another method, but since these are static controls, that seemed a good place to start), dump all Label controls into a generic List object (because you cannot add into an IQueryable).
var controlList = new List<Label>();
foreach (var control in detail.Controls.OfType<Label>())
{
controlList.Add(control);
}
_controls = controlList.AsQueryable();
OR, if you want to get fancy and save a few objects:
_controls = detail.Controls.OfType<Label>().AsQueryable();
At this point, your _controls object has all of the standard LINQ goodness. Our foreach now looks like:
foreach (var indicator in _indicators)
{
var indicatorHeader =
string.Format(“i{0}”, indicator.Id);
var indicatorControl =
_controls.SingleOrDefault(x => x.Name == indicatorHeader);
if (indicatorControl != null)
{
indicatorControl.Text = IsSpanish
? indicator.SpanishText
: indicator.EnglishText;
}
}
The entire block can be wrapped in a try/catch—rather than each “object check”. The report also runs almost 100x faster.




Issam Elbaytam said
Scott Willeke of Data Dynamics posted a response that might be helpful in your scenario. You can read it here
http://blog.scott.willeke.com/2008/07/performance-golden-hammer-linq.html
David said
@Issam-
Thanks for the head’s up regarding Scott’s post. I responded back on his blog–he has some excellent performance points!
I’ll write up a response in a new post later today after a bit more code testing.