Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a custom aggregate function in C# using LINQ

Tags:

c#

linq

c#-4.0

Consider the following class:

public class TaxType
{
    public int Id {get;set;}
    public decimal TotalTaxCollected {get;set;}
    public string DetailXml {get;set;}
}

I got the following LINQ query somewhere in the code:

GetTaxTypesFromTheDataSource()
.Where(/*blah blah blah*/)
.GroupBy(t => new { t.Id })
.Select(g => new TaxType
{
    TotalTaxCollected = g.Sum(n => n.TotalTaxCollected),
    DetailXml = g.Aggregate(SumOfInnerElementsOfXml).DetailXml
})
.ToList();

Where SumOfInnerElementsOfXml is declared as follows:

private TaxType SumOfInnerElementsOfXml(TaxType t1, TaxType t2)
{
    //(a) Deserialize the t1.DetailXml & t2.DetailXml into 2 different arrays.
    //(b) Aggregate both arrays based on their IDs.
    //(c) Serialize the result into an xml string.
    //(d) Instantiate a new TaxType and set its DetailXml to the generated xml string.
    //return (d)
}

The above solution works fine, however I'd like to create my own aggregate function so that I can use it as follows:

GetTaxTypesFromTheDataSource()
.Where(/*blah blah blah*/)
.GroupBy(t => new { t.Id })
.Select(g => new TaxType
{
    TotalTaxCollected = g.Sum(n => n.TotalTaxCollected),
    DetailXml = g.MyCustomAggregateFunction(n => n.DetailXml)
})
.ToList();

How's that possible? I've tried several extension methods but unfortunately none of them worked.

like image 320
Joe Bank Avatar asked Sep 14 '26 13:09

Joe Bank


1 Answers

Your initial solution seems pretty fine to me. However, if you don't like it...

To create a predefined function (not lambda), I'd recommend first to get rid of anonymous type in your query - change:

.GroupBy(t => new { t.Id })

to

.GroupBy(t => t.Id)

This is also generally better, as you get rid of one boxing/unboxing per TaxType and transform IGrouping<{anonymous},TaxType> into IGrouping<int,TaxType> (easier to understand the semantics if someone inspects the code later)

Then, you can declare the method as follows:

public static class Extensions {
    public static string MyCustomAggregateFunction(this IGrouping<int,TaxType> source, Func<TaxType,string> selector) {
        // blah-blah-blah
        //return something
    }
}

You can later make it generic if the need arises.

I'd also recommend getting rid of the dependency on IGrouping, as you later may need to apply that aggregation elsewhere:

TotalTaxCollected = g.Sum(n => n.TotalTaxCollected),
DetailXml = g.Select(n => n.DetailXml).AggregateTaxXML()

and

public static string AggregateTaxXML(this IEnumerable<string> source) {
    // blah-blah-blah
    //return something
}

Sources/further reading: MSDN

like image 175
DarkWanderer Avatar answered Sep 16 '26 03:09

DarkWanderer



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!