How do I reuse LINQ aggregation queries on a set of generic C# types with a common shape or interface that is known at compile-time without resorting to black magic dynamic ExpressionBuilder
stuff?
For example,
// Each "reportable" class has a decimal property to sum over:
interface IReportable<T> {
Expression<T, decimal> GetSummingExpr(); // no static interface methods :(
}
public static class ReportingExtensions {
public static IQueryable<decimal> Report<T>(this IQueryable<T> qry)
{
return x => x.Sum(qry.GetSummingExpr()); // unsure if interfaces can do this
}
}
var invoices = new Invoice [] { Invoice(100.0M), ... };
var quotes = new Quote [] { Quote(50.0M), ... };
// Ideal use-cases:
var invoiceReport = invoices.AsQueryable().Report();
var quoteReport = quotes.AsQueryable().Report();
// "Reportable" class implementations:
public class Invoice : IReportable<Invoice>
{
public decimal InvoiceTotal { get; set; }
public Expression<Func<Invoice, decimal>> GetTotalExpr()
{
return x => x.InvoiceTotal;
}
}
public class Quote : IReportable<Quote>
{
public decimal QuoteTotal { get; set; }
public Expression<Func<Quote, decimal>> GetTotalExpr()
{
return x => x.QuoteTotal;
}
}
I have tried various approaches, but I can't get it to work without introducing magic strings, ala x.GroupBy("InvoiceTotal")
.
To my limited knowledge, this stuff is hard because LINQ types are resolved before generics are "rendered" to "concrete types". However, all of the type signatures are known at compile-time, so it seems like something I should be able to coerce the compiler to do.