using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Runtime.CompilerServices; namespace InABox.Core { /* * Heres what we want to model - a view that performs in-table summaries. Currently we can define out-of-table lookups that sumamrise the values in another table, * but these all seem to generate multi-table queeries, which lose the performance benefits we're looking for. The below "target" query is a single-table query * select SUMMARYTABLE.* from (SELECT [location.id], [product.id], [style.id], [job.id], [dimensions.unit.id], [dimensions.Quantity], [dimensions.length], [dimensions.width], [dimensions.Height], [dimensions.weight], [dimensions.value], [dimensions.unitsize], sum(coalesce([RECEIVED],0.0)-coalesce([ISSUED],0.0)) AS Total, sum(case when [JOBREQUISITIONITEM.ID] is null THEN coalesce([RECEIVED],0.0)-coalesce([ISSUED],0.0) ELSE 0.0 end) AS Available, sum(case when [JOBREQUISITIONITEM.ID] is not null THEN coalesce([RECEIVED],0.0)-coalesce([ISSUED],0.0) ELSE 0.0 end) AS Allocated, sum((coalesce([RECEIVED],0.0)-coalesce([ISSUED],0.0)) * coalesce(COST,0.0)) AS TotalCost, sum((coalesce([RECEIVED],0.0)-coalesce([ISSUED],0.0)) * coalesce(COST,0.0)) / sum(coalesce([RECEIVED],0.0)-coalesce([ISSUED],0.0)) AS AverageCost FROM STOCKMOVEMENT GROUP BY [location.id], [product.id], [style.id], [job.id], [dimensions.unit.id], [dimensions.Quantity], [dimensions.length], [dimensions.width], [dimensions.Height], [dimensions.weight], [dimensions.value], [dimensions.unitsize] ) as SUMMARYTABLE WHERE SUMMARYTABLE.[PRODUCT.ID]='fc159c37-6a59-410b-b15c-8baed75349aa' */ public enum AutoEntitySummaryAggregate { Sum, Average, Count, Minimum, Maximum } public interface IAutoEntitySummaryAggregateColumn : IColumn { AutoEntitySummaryAggregate Aggregate { get; } IComplexFormulaGenerator Definition { get; } } public class AutoEntitySummaryAggregateColumn : Column, IAutoEntitySummaryAggregateColumn { public AutoEntitySummaryAggregate Aggregate { get; } public ComplexFormulaGenerator Definition { get; } IComplexFormulaGenerator IAutoEntitySummaryAggregateColumn.Definition => Definition; public AutoEntitySummaryAggregateColumn(Expression> expression, AutoEntitySummaryAggregate aggregate, ComplexFormulaGenerator definition) : base(expression) { Aggregate = aggregate; Definition = definition; } } public interface IAutoEntitySummaryIDColumn : IColumn { IColumn Source { get; } } public class AutoEntitySummaryIDColumn : Column, IAutoEntitySummaryIDColumn { public Column Source { get; } IColumn IAutoEntitySummaryIDColumn.Source => Source; public AutoEntitySummaryIDColumn(Expression> expression, Expression> source) : base(expression) { Source = new Column(source); } } public interface IAutoEntitySummaryGenerator : IAutoEntityGenerator { new IAutoEntitySummaryIDColumn[] IDColumns(); IAutoEntitySummaryAggregateColumn[] AggregateColumns(); IFilter? HavingFilter { get; } Type SourceType { get; } } public abstract class AutoEntitySummaryGenerator : IAutoEntitySummaryGenerator { public AutoEntitySummaryGenerator() { Configure(); } public bool Distinct => false; public Type Definition => typeof(TInterface); public Type SourceType => typeof(TType); private List _idColumns = new List(); private List _aggregateColumns = new List(); private Filter? _filter = null; public abstract void Configure(); protected void GroupBy(Expression> column, Expression> source) => _idColumns.Add(new AutoEntitySummaryIDColumn(column,source)); protected void Aggregate(Expression> column,AutoEntitySummaryAggregate aggregate, ComplexFormulaGenerator formula) => _aggregateColumns.Add(new AutoEntitySummaryAggregateColumn(column,aggregate, formula)); protected void Having(Filter filter) => _filter = filter; public IAutoEntitySummaryIDColumn[] IDColumns() => _idColumns.OfType().ToArray(); public IAutoEntitySummaryAggregateColumn[] AggregateColumns() => _aggregateColumns.ToArray(); public IFilter? HavingFilter => _filter; IColumn[] IAutoEntityGenerator.IDColumns => _idColumns.ToArray(); } }