I\'m trying to rewrite some old SQL into LINQ to SQL. I have a sproc with a GROUP BY WITH ROLLUP but I\'m not sure what the LINQ equivalent would be. LINQ has a GroupBy bu
I figured out a much simpler solution. I was trying to make it way more complicated than it needed to be. Rather than needing 3-5 classes/methods I only need one method.
Basically, you do your sorting and grouping yourself and then call WithRollup()
to get a List<>
of the items with sub-totals and a grand total. I couldn't figure out how to generate the sub-totals and grand total on the SQL side so those are done with LINQ to Objects. Here's the code:
///
/// Adds sub-totals to a list of items, along with a grand total for the whole list.
///
/// Group and/or sort this yourself before calling WithRollup.
/// Given a TElement, return the property that you want sub-totals for.
/// Given a group of elements, return a TElement that represents the sub-total.
/// A TElement that represents the grand total.
public static List WithRollup(this IEnumerable elements,
Func primaryKeyOfElement,
Func, TElement> calculateSubTotalElement,
TElement grandTotalElement)
{
// Create a new list the items, subtotals, and the grand total.
List results = new List();
var lookup = elements.ToLookup(primaryKeyOfElement);
foreach (var group in lookup)
{
// Add items in the current group
results.AddRange(group);
// Add subTotal for current group
results.Add(calculateSubTotalElement(group));
}
// Add grand total
results.Add(grandTotalElement);
return results;
}
And an example of how to use it:
class Program
{
static void Main(string[] args)
{
IQueryable dataItems = (new[]
{
new CustomObject { City = "Seattle", Plan = "Plan B", Charges = 20 },
new CustomObject { City = "Seattle", Plan = "Plan A", Charges = 10 },
new CustomObject { City = "Seattle", Plan = "Plan B", Charges = 20 },
new CustomObject { City = "Seattle", Plan = "Plan A", Charges = 10 },
new CustomObject { City = "Seattle", Plan = "Plan A", Charges = 10 },
new CustomObject { City = "Seattle", Plan = "Plan A", Charges = 10 },
new CustomObject { City = "Portland", Plan = "Plan A", Charges = 10 },
new CustomObject { City = "Portland", Plan = "Plan A", Charges = 10 },
new CustomObject { City = "Portland", Plan = "Plan C", Charges = 30 },
new CustomObject { City = "Portland", Plan = "Plan C", Charges = 30 },
new CustomObject { City = "Portland", Plan = "Plan C", Charges = 30 }
}).AsQueryable();
IQueryable orderedElements = from item in dataItems
orderby item.City, item.Plan
group item by new { item.City, item.Plan } into grouping
select new CustomObject
{
City = grouping.Key.City,
Plan = grouping.Key.Plan,
Charges = grouping.Sum(item => item.Charges),
Count = grouping.Count()
};
List results = orderedElements.WithRollup(
item => item.City,
group => new CustomObject
{
City = group.Key,
Plan = "All",
Charges = group.Sum(item => item.Charges),
Count = group.Sum(item => item.Count)
},
new CustomObject
{
City = "All",
Plan = "All",
Charges = orderedElements.Sum(item => item.Charges),
Count = orderedElements.Sum(item => item.Count)
});
foreach (var result in results)
Console.WriteLine(result);
Console.Read();
}
}
class CustomObject
{
public string City { get; set; }
public string Plan { get; set; }
public int Count { get; set; }
public decimal Charges { get; set; }
public override string ToString()
{
return String.Format("{0} - {1} ({2} - {3})", City, Plan, Count, Charges);
}
}