Creating Custom Summery Operands

[Infragistics] Devin Rader / Thursday, January 7, 2010

The 2009.2 release of the XamWebGrid added the ability to display a summary row in the grid which allows you to display summary information for columns in the grid.  The feature includes five summary native summary operands (minimum, maximum, sum, count, average), but is also designed to allow you to easily create and display other summary calculations by creating custom summary Operands.

In this post I will demonstrate how to create a custom summery operand which calculates the standard deviation of using the funds Rate of Return values for the past 36 months in order to determine a mutual funds risk.  Using the standard deviation value based on a mutual funds Rate of Return is a common way to measure the risk of a mutual fund. 

To get started, I created a Silverlight Navigation application and added a MutualFundInfo class to it.  This class contains a RateOfReturn property which contains the values I want to summarize.  In the NavigatedTo event of the Home page, I instantiate a List of MutualFundInfo objects and bind that list to a XamWebGrid.

Creating Simple Custom Summary Operands

To get started creating the custom operand, I first create a new class that derives from Infragistics.Silverlight.SummaryOperandBase.

public class StandardDeviationOperand : SummaryOperandBase
{
    protected override string DefaultRowDisplayLabel
    {
        get
        {
            return "Std";
        }
    }
    protected override string DefaultSelectionDisplayLabel
    {
        get { return "Standard Deviation"; }
    }

    public override SummaryCalculatorBase SummaryCalculator
    {
        get
        {
            return new StandardDeviation();
        }
    }
}

This base class includes three methods I need to override:

  1. The DefaultRowDisplayLabel property returns the string I want displayed in the Summary Row when the summary is enabled. 
  2. The DefaultSelectionDisplayLabel property returns the string I want shown to the end user in the selection dropdown list. 
  3. Finally, the SummaryCalculator property, which returns an instance of the class that contains the actual summary operand calculator.

Once I create the Operand I create the summary calculator by deriving a new class from Infragistics.Silverlight.SynchronousSummaryCalculator.  This class includes one overrideable method called Summarize:

public class StandardDeviation : SynchronousSummaryCalculator
{
    public override object Summarize(IQueryable data, string fieldKey)
    {
    }
}

The Summerize method provides me with two method parameters, data an IQueryable that contains all of the data bound to the grid, and fieldKey, which is the name of the column that is executing the Summary.  The method expects me to use the IQueryable object to calculate standard deviation (formula shown below) and return an object that represents the summery result. 

To solve this formula, I can it down into three calculations:

  • Calculate the average Rate of Return using all of the values of the period
  • Sum together the square of the Rate of Return minus the previously calculated average for each value in the period
  • Finally, take the square root of the sum divided by the number of values in the period

 

I can use standard LINQ extension methods to perform most of the calculations, but to be able to use them, I first have to cast the IQueryable to a type that I can execute queries against.  To do that I can use the IQueryable’s Cast method to cast the data object to an IQueryable<T>, where T is my MutualFundInfo type. Once I’ve cast the IQueryable, I can solve the standard deviation formula and return its result.

public override object Summarize(IQueryable data, string fieldKey)
{
    IQueryable<MutualFundInfo> funds = data.Cast<MutualFundInfo>();

    double avg = funds.Average(d => d.RateOfReturn);

    double sum = funds.Sum(d => Math.Pow(d.RateOfReturn - avg, 2));

    return Math.Sqrt(sum / (funds.Count() - 1));
}

Thats all that is needed to create the operand.  Now to use it, I simply add it to the SummaryOperands collection on my grid column:

<iggrid:XamWebGrid x:Name="xamWebGrid1" AutoGenerateColumns="False">
    <iggrid:XamWebGrid.SummaryRowSettings>
        <iggrid:SummaryRowSettings AllowSummaryRow="Bottom" />
    </iggrid:XamWebGrid.SummaryRowSettings>
    <iggrid:XamWebGrid.Columns>
        <iggrid:TextColumn Key="Name" />
        <iggrid:TextColumn Key="Month" />
        <iggrid:TextColumn Key="RateOfReturn">
            <iggrid:TextColumn.SummaryColumnSettings>
                <iggrid:SummaryColumnSettings>
                    <iggrid:SummaryColumnSettings.SummaryOperands>
                        <iggrid:AverageSummaryOperand IsApplied="True" />
                        <local:MutualFundStandardDeviationOperand 
                                  IsApplied="True" />
                    </iggrid:SummaryColumnSettings.SummaryOperands>
                </iggrid:SummaryColumnSettings>
            </iggrid:TextColumn.SummaryColumnSettings>
        </iggrid:TextColumn>
    </iggrid:XamWebGrid.Columns>
</iggrid:XamWebGrid>

Running the demo you can see that the operand shows in the Summary dropdown and the value shows in the footer.

Creating Generic Custom Summary Operands

Of course, one significant drawback to the sample shown above is that it is tightly tied to this specific application because it requires me to explicitly reference the MutualFundInfo object in the Summarize method, as well as know ahead of time about the RateOfReturn property.  If I wanted to reuse this same operand in a different grid, I would have to create an new operand that references the type bound to that new grid, and figure out a way to dynamically reference the right property to run queries against.  Fortunately, with a bit more code, I can change this operand so that it can be applied to any numeric column.

In order to make this happen, there are two primary problems I have to solve:

  1. How do I cast the IQueryable if I don’t know the type bound to the XamWebGrid until runtime?
  2. How do I execute the calculations on the right property of the IQueryable<T>, since I won’t know that until runtime?

Casting IQueryable to IQueryable<T>

To solve the first issue, I basically have to figure out a way to cast IQueryable, which is what is passed into the Summarize method, to IQueryable<T>, which is what I can run my calculations on.  In the previous sample, this was easy because I simply used the Cast method:

IQueryable<T> list = data.Cast<T>();

This obviously won’t work if I don’t know what T is until runtime.  The IQueryable object tells me what its underlying type is through its ElementType property, but since I cannot access that until runtime, there is no way for me to use that to call the Cast<T> method.  What I need to be able to do is call Cast<T>, where T is dynamically generated at runtime based on the value of the ElementType property.  To do this I can use reflection to create a new instance of a generic class at runtime, then I can use that classes generic type to perform my cast.

To get started doing this, inside of my summary calculator class, I create a base class called PlaceholderBase.  I need this non-generic base class so that I have a type which can hold my dynamically instantiated generic type.  The PlaceholderBase include a single abstract method called GetValue:

public abstract class PlaceholderBase
{
    public abstract object GetValue(IQueryable data, string field);
}

Next, I create a generic type Placeholder<T> that derives from PlaceholderBase. In this Placeholder class I override the GetValue method and perform the actual summary calculation:

public class Placeholder<T> : PlaceholderBase
{
    public override object GetValue(IQueryable data, string field)
    {
        return null;
    }
}

Now that I have the Placeholder classes created, I can use the System.Type.MakeGenericType() method to create a new instance of Placeholder<T>.  The MakeGenericType method allows me to pass in the type I want to use for T, which I can get from the IQueryable object:

public override object Summarize(IQueryable data, string fieldKey)
{
    PlaceholderBase plc = 
        Activator.CreateInstance(typeof(Placeholder<>).MakeGenericType(
            new System.Type[] { data.ElementType })) as PlaceholderBase;

    return plc.GetValue(data, fieldKey);            
}

Once I’ve create the generic Placeholder Type, I can use the Activator.CreateInstance method to create a new instance of it.  Again, because I cannot define a Placeholder<T> variable ahead of time, the newly created instance is held in a variable of the base type PlaceholderBase.

Now that I’ve created a way to create Placeholder<T>, I can call the GetValue() method.  Inside the that method, I can cast the IQueryable to IQueryable<T>, where T is the generic type of my Placeholder<T> type held in the ElementType property.

public class Placeholder<T> : PlaceholderBase
{
    public override object GetValue(IQueryable data, string field)
    {
        IQueryable<T> query = data.Cast<T>();
        return null;
    }
}

Using LINQ Expressions to Run Summary Calculations

Once I have an object I can query, I am once step closer to being able to perform the summary calculations, but I still have the problem that I don’t know until runtime which field of the IQueryable<T> object the calculations should be performed on.  Because I won’t know this until runtime, I have to solve this problem again use reflection along with creating and wrapping together a dynamic LINQ Expression which I can execute.

To solve for the standard deviation, I can break the formula down into the same three steps I described in the first part of the post.  The first step is to calculate the average of the values in the field I need to sum.  To do this, just as I did in the simple sample earlier I can run the Average extension method on the IQueryable<T>, passing into it a Lambda expression indicating which property to execute on.  However in this case, instead of being to code that expression ahead of time, I have to do it all dynamically at runtime:

PropertyInfo pi = query.ElementType.GetProperty(field);
ParameterExpression param = Expression.Parameter(query.ElementType, "d");
Expression prop = Expression.Property(param, pi);

This code uses the field method parameter and reflection to get the PropertyInfo object for the field of IQueryable<T> I want to run the calculation on.  It then creates two expressions, one that represents the Type I want to run the query on, and one that represents property of that type I want to query.  Using these two expressions, I can create a Lambda expression which tells the Average method how to calculate the average of the summary column:

query.Average(Expression.Lambda<Func<T, int>>(prop, param)),typeof(double))

Now there is a small problem with this expression, the Lambda requires that I give it the specific type of the property I want it to execute on.  The snippet above assumes that the property I want to Average is of type Int, but that not always be the case, and if I really want this to be a generic Operand, I need to plan to handle any numeric type.  To do this, I can simply test the properties return type beforehand and create different expressions based to handle different numeric types:

if (pi.PropertyType == typeof(int))
{
    d = query.Average(Expression.Lambda<Func<T, int>>(prop, param));
}

If I wanted to support several different numeric types, I could simply write several if statements to test for each numeric type I want to support.

Next, I need to sum the square of each summery field value minus the average.  I can do this in three sub-steps:

  1. Calculating the summary field value minus the average
  2. Taking that value to a power
  3. Summing. 

 

To start I need to define a new BinaryExpression x that represents the subtraction operation.  To create x, I use the Expression.Subtract method:

BinaryExpression x = Expression.Subtract(
        prop, 
        Expression.Constant(query.Average(
            Expression.Lambda<Func<T, long>>(prop, param)),typeof(double)
        ));

The Expression.Subtract method requires me to provide the left and right expressions, which in this case are the property expression prop created earlier and the Average calculation I created earlier (which I convert into a ConstantExpression).

Once the expression is created, I can continue to the second step by wrapping the BinaryExpression in an expression which raises the value to a power.  To do this, I can use the Expression.Call method, which allows me to create an expression that calls a method, in this case Math.Pow().   Expression.Call requires me to pass in two parameters: a MethodInfo object representing the method I want to execute, and an array of expressions which represent that methods input parameters.  Getting the MethodInfo is easy using reflection:

MethodInfo power = 
    typeof(Math).GetMethod("Pow", new Type[] { typeof(double), typeof(double) });

Now I have to provide the Math.Pow input parameters.  The method requires two input parameters; the first is the a value to raise to a power which is represented by the BinaryExpression I create earlier, the second is the power value which I can create using another ConstantExpression:

ConstantExpression y = Expression.Constant(2.0, typeof(double));

Putting everything together, the Expression.Call method looks like this:

Expression lambda = Expression.Call(null, power, new Expression[] { x, y });

Now I can execute the last part of this step which is to use the Sum extension method to sum the values of the

var sum = query.Sum(Expression.Lambda<Func<T, double>>(lambda, param));

I simply pass the expression ‘lambda’ which I have been building up into the method, along with the object type it will execute against and I can now calculate the sum of the square of the field value minus the average. (whew!)

The last step in calculating the standard deviation is to take the square root of the sum I just calculated divided by the number of items in the field, minus 1.  This is simple using the Math.Sqrt method:

return Math.Sqrt((sum) / (query.Count()-1));

Once I calculate the final value I can return it as the methods return value which will be displayed by the grid in its summary row.

Conclusion

By default the XamWebGrid makes it very easy to add basic summary calculations to your application by including a variety of common summary calculators, but there may be times where you need to add in your own custom summary calculators.  As was shown in this post, the control makes it easy to do this by providing you with the base classes and virtual methods you need to implement to quickly and easily create your own custom summary calculators for columns in your grid.  The post also showed how by using a bit of reflection and LINQ Expressions its possible to create your own custom summary calculators that are generic enough to be used by any column in and of your grids.

You cna download the complete sample source here.  Note that you will need to have NetAdvantage Web Client 9.2 or later installed in order to build the sample.