|
Article relates to
|
Telerik Reporting
|
|
Created by
|
Rossen Hristov
|
|
Last modified
|
12 April 2011
|
|
Last modified by
|
Peter
|
HOW-TO
Implement and use custom aggregate functions in Telerik Reporting.
SOLUTION
Along with the standard aggregate functions provided by Telerik Reporting, developers can define their own custom aggregate when needed. For the sake of this article let us assume that we need to create and use the
Count Distinct aggregate function. To achieve that the developer needs to do a couple of things:
User Defined Aggregate Class
In order to separate the aggregate function logic from the report definition and to achieve better re-usability the developer needs to implement an aggregate class. User aggregates are public or internal (Public or Friend in VB.NET) classes that implement the
IAggregateFunction interface.
This basic class needs to implement the following functionality:
-
Init: Called every time the accumulation of values must start over for a new subset of records from the data source.
- Accumulate: Accumulates new argument values to the current aggregate function.
- Merge: Merges the specified aggregate function to the current one.
- GetValue: Returns the currently accumulated value of the aggregate function.
To better explain the pattern described above, let us take a look at the implementation of a class that counts distinct values:
[C#]
[AggregateFunction(Name = "CountDistinct",
Description = "Defines an aggregate function to count distinct values.")]
public class CountDistinctAggregate :
Telerik.Reporting.Expressions.IAggregateFunction
{
private System.Collections.Generic.List<object> distinctValues;
/// <summary>
/// Initializes the current aggregate function to its initial
/// state ready to accumulate and merge values.
/// </summary>
/// <remarks>
/// This method is called every time the accumulation of values
/// must start over for a new subset of records from the data source.
/// </remarks>
public void Init()
{
this.distinctValues = new System.Collections.Generic.List<object>();
}
/// <summary>
/// Accumulates new argument values to the current aggregate function.
/// </summary>
/// <remarks>
/// This aggregate function accepts one argument:
/// number - a numeric value to accumulate to the aggregate function;
/// </remarks>
public void Accumulate(object[] values)
{
if (!distinctValues.Contains(values[0]))
{
distinctValues.Add(values[0]);
}
}
/// <summary>
/// Merges the specified aggregate function to the current one.
/// </summary>
/// <param name="Aggregate">
/// Specifies an aggregate function to be merged to the current one.
/// </param>
/// <remarks>
/// This method allows the reporting engine to merge two accumulated
/// subsets of the same aggregate function into a single result.
/// </remarks>
public void Merge(IAggregateFunction aggregate)
{
// Accumulate the values of the specified aggregate function.
System.Collections.Generic.List<object> sums1 = ((CountDistinctAggregate)aggregate).distinctValues;
foreach(object o in sums1)
{
this.Accumulate(new object[] { o });
}
}
/// <summary>
/// Returns the currently accumulated value of the aggregate function.
/// </summary>
/// <returns>
/// The currently accumulated numeric value of the aggregate function.
/// </returns>
public object GetValue()
{
return this.distinctValues.Count;
}
}
[VB.NET]
<Expressions.AggregateFunction(Name:="CountDistinct", Description:="Defines an aggregate function to count distinct values.")> _
Public Class CountDistinctAggregate
Implements Telerik.Reporting.Expressions.IAggregateFunction
Private distinctValues As System.Collections.Generic.List(Of Object)
''' <summary>
''' Initializes the current aggregate function to its initial state
''' ready to accumulate and merge values.
''' </summary>
''' <remarks>
''' This method is called every time the accumulation of values must
''' start over for a new subset of records from the data source.
''' </remarks>
Sub Init() Implements Expressions.IAggregateFunction.Init
Me.distinctValues = New System.Collections.Generic.List(Of Object)()
End Sub
''' <summary>
''' Accumulates new argument values to the current aggregate function.
''' </summary>
''' <remarks>
''' This aggregate function accepts one argument:
''' number - a numeric value to accumulate to the aggregate function;
''' </remarks>
Sub Accumulate(ByVal values() As Object) Implements Expressions.IAggregateFunction.Accumulate
If Not distinctValues.Contains(values(0)) Then
distinctValues.Add(values(0))
End If
End Sub
''' <summary>
''' Merges the specified aggregate function to the current one.
''' </summary>
''' <param name="Aggregate">
''' Specifies an aggregate function to be merged to the current one.
''' </param>
''' <remarks>
''' This method allows the reporting engine to merge two accumulated
''' subsets of the same aggregate function into a single result.
''' </remarks>
Sub Merge(ByVal aggregate As Expressions.IAggregateFunction) Implements Expressions.IAggregateFunction.Merge
' Accumulate the values of the specified aggregate function.
Dim sums1 = DirectCast(aggregate, CountDistinctAggregate).distinctValues
For Each o As Object In sums1
Me.Accumulate(New Object() {o})
Next
End Sub
''' <summary>
''' Returns the currently accumulated value of the aggregate function.
''' </summary>
''' <returns>
''' The currently accumulated numeric value of the aggregate function.
''' </returns>
Function GetValue() As Object Implements Expressions.IAggregateFunction.GetValue
Return Me.distinctValues.Count
End Function
End Class
Using the Custom Aggregate in a Report
Apply
AggregateFunctionAttribute to the custom aggregate class implementation to define an interface for the users of the aggregate function. The Name parameter of the attribute defines how to refer to the function in expressions.
And here is what our report might look like. The data is sample. You can play around with the sample attached report: