Custom Report Items Processing
In This Topic
The Microsoft Reporting Services documentation is sparse for custom report items and the way in which they are processed. On the other hand you as a user need to know the details about this process - this will help you use expressions, groupings, filters and sortings more efficiently. It will also help you understand the context in which certain expressions are evaluated.
Disclaimer: The content of this topic is based on Nevron Software internal research. It is provided "AS IS" and with no warranties for completeness and 100% accuracy.
The topic is for advanced users of MS Reporting Services and although not mandatory, some programming experience will help you understand the pseudo code of the algorithm. We will first start with some terms and definitions and then will describe the processing algorithm in detail.
Terms and Definitions
A custom report item in reporting services has a design model and runtime state. The design model is processed towards a data set to produce a runtime representation of that model for the actual data contained in the data set. It is important to know that for the same design model the runtime state can be different, depending on the data set which is processed.
The following images illustrate a simple custom report item at design time and runtime:
Figure 1. Design Time |
Figure 2. Runtime |
|
|
In Figure 1 you can see a sample custom report item at design time. It is assembled from properties, filters, hierarchically organized row and col data groupings and a grid of data cells, which is sized to the count of leaf row/col data groupings.
In Figure 2 you can see a sample custom report item at runtime. It is produced for the design model at Figure 1. Assuming that the A, B, E and F data groupings are dynamic and produced only 2 data members each and also assuming that the C, D, G and H data groupings are static. The color coding helps you see the span of each data cell.
Following is a definition of the terms used in custom report items:
data grouping and data members - a data grouping is either dynamic or static. A dynamic data grouping has groupings, sortings, filters and properties. A static data grouping has only properties. When a dynamic data grouping is processed it creates one or more data members. When a static data grouping is processed it always creates a single data member.
data cell - a data cell is a container for properties. At runtime, the properties of a single design time data cell are evaluated and copied to all the intersections of the row/col data members created for the leaf row/col data groupings, for which the design time data cell was created.
property - at design time a property is a a {name:expression} pair. At runtime a property is {name:value} pair, where the value is taken from the result of the expression evaluation. A custom report item can store properties in three places: in the report item properties, in a data grouping and in a data cell.
data set and fields - a data set is any tabular data source the columns of which are called fields.
recordset - a recordset is an enumeration of a subset of records (rows) from a data set. A recordset is EMPTY, if it contains no records. A recordset is FULL, if it contains all the records from the data set. The intersection of recordsets R1 and R2 is a recordset, which contains records that exist in both R1 and R2.
expression - an expression is a formula string, which is evaluated towards a recordset (e.g. the same expression may evaluate to a different value given a different input recordset). Expressions are used at design time in properties, groupings, sortings and filters.
Processing Algorithm
The processing algorithm is composed by several routines, which are described below. The entry point is the ProcessCustomReportItem routine which is described last.
Intersect
input
RS1 - input recordset 1
RS2 - input recordset 2
output
intersectionRS - recordset, which represents the intersection of the input recordsets
Filter
input
inputRS - input recordset
filters[] - collection of data grouping filters
output
filteredRS - subset of the input recordset the records of which match the filters
description
In order for a record from the inputRS to be included in the filteredRS all filters must evaluate to true. A filter is a predicate, which contains a condition expression, operator and value(s). The condition expression result is compared with an operator towards the values, which may also be expressions. Both the condition and values expressions are evaluated towards the currently filtered record (e.g. towards a single record recordset).
Group
input
inputRS - input recordset
groupByExpressions - collection of data grouping group by expressions.
output
groupRecordsets[] - collection of recordsets, which are the groups inside inputRS produced by the grouping
description
The groupingExpressions are evaluated towards each record of the inputRS. A group recordset is created for each subset of the inputRS for which all grouping expressions evaluated to the same value.
Sort
input
recordsets[] - collection of input recordsets to sort
sortings[] - collection of data grouping sortings
output
sortedRecordsets[] - sorted collection of the input recordsets
description
Each sorting is a pair of expression and direction (ascending or descending). For each pair of input recordsets R1 and R2 the sortings define their order (R1 < R2, R1 = R2 or R1 > R2) by evaluating the sorting expression towards R1 and R2 and comparing the resulting values. If the values are equal the operation is performed for the next sorting. The input recordsets are sorted given this order.
Evaluate
input
properties - a collection of {name:expression} pairs
recordset - recordset towards which to evaluate
output
properties - a collection of {name:value} pairs
description
Evaluates all properties towards the specified recordset and returns the evaluated properties.
UpdateDataCellSpan
input
dataGrouping - leaf data grouping
dataMember - leaf data member
recordset - leaf row/col recordset
description
Updates the data cell span information. This routine is called for each processed leaf row or col data grouping. The method builds up the information needed to resize the runtime data cells grid, stores information about the rows/cols span of a data cell and stores the leaf row and col recordsets. This information is used by the final step of the ProcessCustomReportItem routine.
ProcessDataGrouping
input
dataGrouping - data grouping which to process and convert to data members
recordset - input recordsets towards which to process the data grouping
output
dataMembers - resulting data members
description
Processes a data grouping towards the recordset and returns a collection of data members. The pseudo code of this routine is:
// for a static data grouping -> create only one data member
if (dataGrouping.IsStatic)
{
dataMember = new DataMember
dataMembers.Add(dataMember)
// evaluate the properties of the data member
dataMember.Properties = Evaluate(dataGrouping.Properties, groupRecordset)
if (dataGrouping.IsLeaf)
{
// in case the data grouping is a leaf, update its row/col span and the intersection recordsets
UpdateDataCellSpan(dataGrouping, dataMember, groupRecordset);
}
else
{
// in case the data grouping has children, recursively process them and add them to the data member
foreach (childDataGrouping in dataGrouping.ChildDataGroupings)
{
dataMember.ChildDataMembers = ProcessDataGrouping(childDataGrouping, groupRecordset)
}
}
return dataMembers
}
// for a dynamic data grouping - first filter the input recordset with the data grouping filters
recordset = Filter(recordset, dataGrouping.Filters)
// create the group recordsets using the data grouping group by expressions
groupRecordsets = Group(recordset, dataGrouping.GroupByExpressions)
// sort the group recordsets with the data grouping sortings
groupRecordsets = Sort(groupRecordsets, dataGrouping.Sortings)
// create a data member for each group recordsets
foreach groupRecordset in groupRecordsets
{
dataMember = new DataMember
dataMembers.Add(dataMember)
// evaluate the properties of the data member
dataMember.Properties = Evaluate(dataGrouping.Properties, groupRecordset)
if (dataGrouping.IsLeaf)
{
// in case the data grouping is a leaf, update its row/col span and the intersection recordsets
UpdateDataCellSpan(dataGrouping, dataMember, groupRecordset);
}
else
{
// in case the data grouping has children, recursively process them and add them to the data member
foreach (childDataGrouping in dataGrouping.ChildDataGroupings)
{
dataMember.ChildDataMembers = ProcessDataGrouping(childDataGrouping, groupRecordset)
}
}
}
return dataMembers
ProcessCustomReportItem
input
designCRI - a custom report item design time structure
output
runtimeCRI - a custom report item runtime structure
description
The custom report item processing is best illustrated in pseudo code:
// initially start with a full recordset
cur = FULL
// evaluate the report item properties
runtimeCRI.Properties = Evaluate(designCRI.Properties, cur)
// filter the recordset with the report item filters
cur = Filter(cur, designCRI.Filters)
// process the col data groupings
foreach dataGrouping in designCRI.ColumnDataGroupings
{
runtimeCRI.AddColumnDataMembers(ProcessDataGrouping(dataGrouping, cur))
}
// process the row data groupings
foreach dataGrouping in designCRI.RowDataGroupings
{
runtimeCRI.AddRowDataMembers(ProcessDataGrouping(dataGrouping, cur))
}
// perform the cross intersection of the data cells the information needed is
// 1. leaf row and col recordset
// 2. design data cell whose properties to evaluate
// this information is accumulated by the UpdateDataCellSpan routine
foreach row in runtimeCRI.Rows
{
foreach col in runtimeCRI.Cols
{
// get the data cell whose properties to evaluate
dataCell = DataCellSpan.GetDataCell(row, col)
// get the row and col recordsets
rowRS = DataCellSpan.GetRowRecordset(row)
colRS = DataCellSpan.GetColRecordset(col)
// intersect the recordsets
cur = Intersect(rowRS, colRS)
// evaluate the data cell towards the intersection recordset
runtimeCRI[row, col] = Evaluate(dataCell, cur)
}
}
Questions and Answers
Now that you know how custom report items are processed it is time to answer some common questions:
Q. Where does Nevron Chart for Reporting Services stores its properties
A. All settings that you see in the Chart Properties > Chart tab are stored in the custom report item properties. All settings that you see in the Values Grouping Editor are stored in the static data grouping representing the values, except the expressions in the Data tab, which are stored in the associated data cell. The category and series data grouping editors store the Label property in the respective dynamic data grouping.
Q. What expressions to use in Report Item Properties (e.g. for properties in the Chart tab)
A. Because the report item properties are evaluated towards a FULL recordset it only makes sense to use aggregates like SUM, MIN, MAX etc. and avoid FIRST. For these properties we generally recommend to avoid field expressions, but rather use expressions, which reference report parameters, globals etc.
Q. What expression to use for the Label of a Category/Series Data Grouping
A. We recommend to use the field on which you group.
Q. What expression to use for the Data Point Values (in the Values Group Editor - Data tab)
A. We recommend to use a field value, since this will be recorded in a data cell.
Q. How to Group and Sort
A. We recommend to group by a single field value and use that single field value in the sorting expression (if necessary). In general you can only sort on what you group (because the grouped recordsets, not records get sorted) - for example: if you group by field A - you can only sort by field A. If you group by fields A and B then you can only sort on A, B or A and B.