diff --git a/Expressions/Expressions.Linq/EnumerableExpressions/GroupJoinExpression.cs b/Expressions/Expressions.Linq/EnumerableExpressions/GroupJoinExpression.cs index d61c6992..c67ca74f 100644 --- a/Expressions/Expressions.Linq/EnumerableExpressions/GroupJoinExpression.cs +++ b/Expressions/Expressions.Linq/EnumerableExpressions/GroupJoinExpression.cs @@ -1,12 +1,13 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq.Expressions; using SL = System.Linq.Enumerable; using NMF.Expressions.Linq; namespace NMF.Expressions { - internal class GroupJoinExpression : IEnumerableExpression + internal class GroupJoinExpression : IEnumerableExpression, IOptimizableEnumerableExpression { public IEnumerableExpression Source { get; set; } public IEnumerable Inner { get; set; } @@ -19,6 +20,13 @@ internal class GroupJoinExpression : IEnumerableE public IEqualityComparer Comparer { get; set; } private INotifyEnumerable notifyEnumerable; + public Expression OptSelectorExpression => ResultSelector; + + public Expression PrevExpression { get; set; } + + public IEnumerableExpression OptSource => Source; + + public GroupJoinExpression(IEnumerableExpression outer, IEnumerable inner, Expression> outerKeySelector, Func outerKeySelectorCompiled, Expression> innerKeySelector, Func innerKeySelectorCompiled, Expression, TResult>> resultSelector, Func, TResult> resultSelectorCompiled, IEqualityComparer comparer) { if (outer == null) throw new ArgumentNullException(nameof(outer)); @@ -62,5 +70,23 @@ INotifyEnumerable IEnumerableExpression.AsNotifiable() { return AsNotifiable(); } + + public IEnumerableExpression AsOptimized(IOptimizableEnumerableExpression expression = null) + { + if (expression != null) + return Merge(expression); + return (IEnumerableExpression)this; + } + + public IEnumerableExpression Merge(IOptimizableEnumerableExpression prevExpr) + { + var mergedSelectorExpression = new ProjectionMergeQueryOptimizer().Optimize(prevExpr.OptSelectorExpression, OptSelectorExpression) as Expression, TOptimizedResult>>; + return new GroupJoinExpression(Source, Inner, OuterKeySelector, null, InnerKeySelector, null, mergedSelectorExpression, null, Comparer); + } + + IOptimizableEnumerableExpression IOptimizableEnumerableExpression.AsOptimized2(IQueryOptimizer queryOptimizer) + { + return queryOptimizer.OptimizeExpression(this); + } } } diff --git a/Expressions/Expressions.Linq/EnumerableExpressions/JoinExpression.cs b/Expressions/Expressions.Linq/EnumerableExpressions/JoinExpression.cs index 28e423bb..230fe594 100644 --- a/Expressions/Expressions.Linq/EnumerableExpressions/JoinExpression.cs +++ b/Expressions/Expressions.Linq/EnumerableExpressions/JoinExpression.cs @@ -1,12 +1,13 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq.Expressions; using SL = System.Linq.Enumerable; using NMF.Expressions.Linq; namespace NMF.Expressions { - internal class JoinExpression : IEnumerableExpression + internal class JoinExpression : IEnumerableExpression, IOptimizableEnumerableExpression { public IEnumerableExpression Source { get; set; } public IEnumerable Inner { get; set; } @@ -19,6 +20,13 @@ internal class JoinExpression : IEnumerableExpres public IEqualityComparer Comparer { get; set; } private INotifyEnumerable notifyEnumerable; + + public IEnumerableExpression OptSource => Source; + + + public Expression OptSelectorExpression => ResultSelector; + public Expression PrevExpression { get; set; } + public JoinExpression(IEnumerableExpression outer, IEnumerable inner, Expression> outerKeySelector, Func outerKeySelectorCompiled, Expression> innerKeySelector, Func innerKeySelectorCompiled, Expression> resultSelector, Func resultSelectorCompiled, IEqualityComparer comparer) { if (outer == null) throw new ArgumentNullException(nameof(outer)); @@ -67,5 +75,23 @@ INotifyEnumerable IEnumerableExpression.AsNotifiable() { return AsNotifiable(); } + + public IEnumerableExpression AsOptimized(IOptimizableEnumerableExpression expression = null) + { + if (expression != null) + return Merge(expression); + return (IEnumerableExpression)this; + } + + public IEnumerableExpression Merge(IOptimizableEnumerableExpression prevExpr) + { + var mergedSelectorExpression = new ProjectionMergeQueryOptimizer().Optimize(prevExpr.OptSelectorExpression, OptSelectorExpression) as Expression>; + return new JoinExpression(Source, Inner, OuterKeySelector, null, InnerKeySelector, null, mergedSelectorExpression, null, Comparer); + } + + IOptimizableEnumerableExpression IOptimizableEnumerableExpression.AsOptimized2(IQueryOptimizer queryOptimizer) + { + return queryOptimizer.OptimizeExpression(this); + } } } diff --git a/Expressions/Expressions.Linq/EnumerableExpressions/OrderByExpression.cs b/Expressions/Expressions.Linq/EnumerableExpressions/OrderByExpression.cs index 3a47c7b0..180c47aa 100644 --- a/Expressions/Expressions.Linq/EnumerableExpressions/OrderByExpression.cs +++ b/Expressions/Expressions.Linq/EnumerableExpressions/OrderByExpression.cs @@ -6,9 +6,13 @@ namespace NMF.Expressions { - internal class OrderByExpression : IOrderableEnumerableExpression + internal class OrderByExpression : IOrderableEnumerableExpression, ISQO { public IEnumerableExpression Source { get; private set; } + + public IEnumerableExpression OptSource => Source; + + public Expression> Predicate { get; private set; } public Func PredicateCompiled { get; private set; } public IComparer Comparer { get; private set; } diff --git a/Expressions/Expressions.Linq/EnumerableExpressions/SelectExpression.cs b/Expressions/Expressions.Linq/EnumerableExpressions/SelectExpression.cs index 04614d85..cfa0bb92 100644 --- a/Expressions/Expressions.Linq/EnumerableExpressions/SelectExpression.cs +++ b/Expressions/Expressions.Linq/EnumerableExpressions/SelectExpression.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Linq.Expressions; using SL = System.Linq.Enumerable; @@ -7,12 +8,19 @@ namespace NMF.Expressions { - internal class SelectExpression : IEnumerableExpression + internal class SelectExpression : IEnumerableExpression, IOptimizableEnumerableExpression { public IEnumerableExpression Source { get; private set; } public Expression> SelectorExpression { get; private set; } public Func SelectorCompiled { get; private set; } + private INotifyEnumerable notifyEnumerable; + public Expression OptSelectorExpression => SelectorExpression; + public Expression PrevExpression { get; set; } + + public IEnumerableExpression OptSource => Source; + + public SelectExpression(IEnumerableExpression source, Expression> selector, Func selectorCompiled) { @@ -27,6 +35,11 @@ public SelectExpression(IEnumerableExpression source, Expression AsNotifiable() { + IEnumerableExpression optimizedExpression = QueryOptimizer.DefaultQueryOptimizer.Optimize(this) as IEnumerableExpression; + + if (optimizedExpression != this) + return optimizedExpression.AsNotifiable(); + if (notifyEnumerable == null) { notifyEnumerable = Source.AsNotifiable().Select(SelectorExpression, SelectorCompiled); @@ -49,5 +62,27 @@ INotifyEnumerable IEnumerableExpression.AsNotifiable() { return AsNotifiable(); } + + public IEnumerableExpression AsOptimized(IOptimizableEnumerableExpression prevOptExpression = null) + { + if (Source is IOptimizableEnumerableExpression source) + return this.Optimize(source).AsOptimized(prevOptExpression); + + if(prevOptExpression != null) + return Merge(prevOptExpression); + + return (IEnumerableExpression) this; + } + + public IEnumerableExpression Merge(IOptimizableEnumerableExpression prevOptExpression) + { + var mergedSelectorExpression = new ProjectionMergeQueryOptimizer().Optimize(prevOptExpression.OptSelectorExpression, OptSelectorExpression) as Expression>; + return new SelectExpression(Source, mergedSelectorExpression, null); + } + + IOptimizableEnumerableExpression IOptimizableEnumerableExpression.AsOptimized2(IQueryOptimizer queryOptimizer) + { + return queryOptimizer.OptimizeExpression(this); + } } } diff --git a/Expressions/Expressions.Linq/EnumerableExpressions/SelectManyExpression.cs b/Expressions/Expressions.Linq/EnumerableExpressions/SelectManyExpression.cs index 02e5bcf8..45a12a15 100644 --- a/Expressions/Expressions.Linq/EnumerableExpressions/SelectManyExpression.cs +++ b/Expressions/Expressions.Linq/EnumerableExpressions/SelectManyExpression.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Linq.Expressions; using SL = System.Linq.Enumerable; @@ -7,7 +8,7 @@ namespace NMF.Expressions { - internal class SelectManyExpression : IEnumerableExpression + internal class SelectManyExpression : IEnumerableExpression, IOptimizableEnumerableExpression { public IEnumerableExpression Source { get; private set; } public Expression>> FuncExpression { get; private set; } @@ -16,6 +17,13 @@ internal class SelectManyExpression : IEnumerab public Func ResultSelectorCompiled { get; private set; } private INotifyEnumerable notifyEnumerable; + public Expression OptSelectorExpression => ResultSelector; + + public IEnumerableExpression OptSource => Source; + + + public Expression PrevExpression { get; set; } + public SelectManyExpression(IEnumerableExpression source, Expression>> func, Func> funcCompiled, Expression> resultSelector, Func resultSelectorCompiled) { if (source == null) throw new ArgumentNullException(nameof(source)); @@ -53,5 +61,23 @@ INotifyEnumerable IEnumerableExpression.AsNotifiable() { return AsNotifiable(); } + + public IEnumerableExpression AsOptimized(IOptimizableEnumerableExpression expression = null) + { + if(expression != null) + return Merge(expression); + return (IEnumerableExpression) this; + } + + public IEnumerableExpression Merge(IOptimizableEnumerableExpression prevExpr) + { + var mergedSelectorExpression = new ProjectionMergeQueryOptimizer().Optimize(prevExpr.OptSelectorExpression, OptSelectorExpression) as Expression>; + return new SelectManyExpression(Source, FuncExpression, null, mergedSelectorExpression, null); + } + + IOptimizableEnumerableExpression IOptimizableEnumerableExpression.AsOptimized2(IQueryOptimizer queryOptimizer) + { + return queryOptimizer.OptimizeExpression(this); + } } } diff --git a/Expressions/Expressions.Linq/EnumerableExpressions/ThenByExpression.cs b/Expressions/Expressions.Linq/EnumerableExpressions/ThenByExpression.cs index ffff69ce..141f1a5a 100644 --- a/Expressions/Expressions.Linq/EnumerableExpressions/ThenByExpression.cs +++ b/Expressions/Expressions.Linq/EnumerableExpressions/ThenByExpression.cs @@ -7,9 +7,13 @@ namespace NMF.Expressions { - internal class ThenByExpression : IOrderableEnumerableExpression + internal class ThenByExpression : IOrderableEnumerableExpression, ISQO { public IOrderableEnumerableExpression Source { get; private set; } + + public IEnumerableExpression OptSource => Source; + + public Expression> Predicate { get; private set; } public Func PredicateCompiled { get; private set; } public IComparer Comparer { get; private set; } diff --git a/Expressions/Expressions.Linq/ExpressionsOptimization/IQueryOptimizer.cs b/Expressions/Expressions.Linq/ExpressionsOptimization/IQueryOptimizer.cs new file mode 100644 index 00000000..100537db --- /dev/null +++ b/Expressions/Expressions.Linq/ExpressionsOptimization/IQueryOptimizer.cs @@ -0,0 +1,15 @@ +namespace NMF.Expressions +{ + internal interface IQueryOptimizer + { + IEnumerableExpression Optimize(ISQO expression); + + IOptimizableEnumerableExpression OptimizeExpression(SelectExpression expression); + + IOptimizableEnumerableExpression OptimizeExpression(SelectManyExpression expression); + + IOptimizableEnumerableExpression OptimizeExpression(JoinExpression expression); + + IOptimizableEnumerableExpression OptimizeExpression(GroupJoinExpression expression); + } +} \ No newline at end of file diff --git a/Expressions/Expressions.Linq/ExpressionsOptimization/OptimizableExpressionExtensions.cs b/Expressions/Expressions.Linq/ExpressionsOptimization/OptimizableExpressionExtensions.cs new file mode 100644 index 00000000..d038c2b5 --- /dev/null +++ b/Expressions/Expressions.Linq/ExpressionsOptimization/OptimizableExpressionExtensions.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; + +namespace NMF.Expressions.Linq +{ + /// + /// Defines a set of extension methods + /// + internal static class OptimizableEnumerableExpressionExtensions + { + internal static IOptimizableEnumerableExpression Optimize(this IOptimizableEnumerableExpression optimizableEnumerableExpression, IOptimizableEnumerableExpression source) + { + return (IOptimizableEnumerableExpression) source.AsOptimized(optimizableEnumerableExpression); + } + } +} \ No newline at end of file diff --git a/Expressions/Expressions.Linq/ExpressionsOptimization/ProjectionMergeQueryOptimizer.cs b/Expressions/Expressions.Linq/ExpressionsOptimization/ProjectionMergeQueryOptimizer.cs new file mode 100644 index 00000000..fb8b8fc2 --- /dev/null +++ b/Expressions/Expressions.Linq/ExpressionsOptimization/ProjectionMergeQueryOptimizer.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +namespace NMF.Expressions +{ + /// + /// Implements query optimization approaches + /// + internal class ProjectionMergeQueryOptimizer : IQueryOptimizer + { + private static Type[] promotionMethodCallTypes = + { + typeof(ObservingFunc<,>), + typeof(ObservingFunc<,,>), + typeof(ObservingFunc<,,,>), + typeof(ObservingFunc<,,,,>), + typeof(ObservingFunc<,,,,,>), + typeof(ObservingFunc<,,,,,,>), + typeof(ObservingFunc<,,,,,,,>), + typeof(ObservingFunc<,,,,,,,,>), + typeof(ObservingFunc<,,,,,,,,,>), + typeof(ObservingFunc<,,,,,,,,,,>), + typeof(ObservingFunc<,,,,,,,,,,,>), + typeof(ObservingFunc<,,,,,,,,,,,,>), + typeof(ObservingFunc<,,,,,,,,,,,,,>), + typeof(ObservingFunc<,,,,,,,,,,,,,,>), + typeof(ObservingFunc<,,,,,,,,,,,,,,,>) + }; + + + /// + /// Optimization of two consecutive expressions into one + /// + /// Source type of firstExpression and Source Type of merged expression + /// Result type of firstExpression + /// Result type of secondExpression and Result Type of merged expression + /// In the query evaluation earlier occurring expression + /// In the query evaluation appearing expression after firstExpression + /// + public Expression Optimize(Expression firstExpression, Expression secondExpression) + { + return MergeLambdaExpressions(firstExpression as LambdaExpression, secondExpression as LambdaExpression); + } + + /// + /// Merging two expressions by inserting secondLambdaExpression into firstLambdaExpression + /// + /// + /// + /// + public Expression MergeLambdaExpressions(LambdaExpression firstLambdaExpression, LambdaExpression secondLambdaExpression) + { + var queryOptimizerVisitor = new QueryOptimizerVisitor(firstLambdaExpression, secondLambdaExpression); + var builtExpression = queryOptimizerVisitor.Visit(firstLambdaExpression) as LambdaExpression; + return Expression.Lambda(builtExpression.Body, secondLambdaExpression.Parameters); + } + + /// + /// Merging two expressions by introducing a variable that represents the second expression + /// + /// Parameter type of firstLambdaExpression and Parameter type of merged expression + /// Result type of firstLambdaExpression + /// Result type of secondLambdaExpression and Result Type of merged expression + /// + /// + /// + public Expression MergeLambdaExpressionsWithVariables(LambdaExpression firstLambdaExpression, LambdaExpression secondLambdaExpression) + { + var introduceOptimizationVaribable = Expression.Parameter(typeof(TResult), "optimization_arg"); + var evaluateMethodArgs = new Expression[secondLambdaExpression.Parameters.Count + 1]; + var queryOptimizerVisitor = new QueryOptimizerVisitor(firstLambdaExpression, secondLambdaExpression) + { + OptimizationVaribable = introduceOptimizationVaribable + }; + + var parametersWithOptimizationVariable = new ParameterExpression[secondLambdaExpression.Parameters.Count + 1]; + for (int i = 0; i < secondLambdaExpression.Parameters.Count; i++) + parametersWithOptimizationVariable[i] = secondLambdaExpression.Parameters[i]; + + parametersWithOptimizationVariable[secondLambdaExpression.Parameters.Count] = introduceOptimizationVaribable; + + var introduceOptimizationExpr = queryOptimizerVisitor.Visit(Expression.Lambda(firstLambdaExpression.Body, parametersWithOptimizationVariable)); + + for (int i = 0; i < secondLambdaExpression.Parameters.Count; i++) + { + evaluateMethodArgs[i] = secondLambdaExpression.Parameters[i]; + } + evaluateMethodArgs[secondLambdaExpression.Parameters.Count] = secondLambdaExpression.Body; + + Type[] genericTypeArgumentsOfExprOfFunc = introduceOptimizationExpr.GetType().GetGenericArguments()[0].GenericTypeArguments; + Type[] genericTypeArgumentsOfExprOfFuncWithoutResultType = new Type[genericTypeArgumentsOfExprOfFunc.Length - 1]; + Array.Copy(genericTypeArgumentsOfExprOfFunc, 0, genericTypeArgumentsOfExprOfFuncWithoutResultType, 0, genericTypeArgumentsOfExprOfFunc.Length - 1); + + + var observingFunc = CreateObservingFunc(genericTypeArgumentsOfExprOfFunc, introduceOptimizationExpr); + var evaluateMethodInfo = observingFunc.GetType().GetMethod("Evaluate", genericTypeArgumentsOfExprOfFuncWithoutResultType); + + return Expression.Lambda( + Expression.Call( + Expression.Constant( + observingFunc + ), + evaluateMethodInfo, + evaluateMethodArgs + ), + secondLambdaExpression.Parameters + ); + } + + public T CastExamp1(object input) + { + return (T)input; + } + + private object CreateObservingFunc(Type[] types, dynamic introduceOptimizationExpr) + { + var promotionMethodCallType = promotionMethodCallTypes[types.Length-2].MakeGenericType(types); + var constructor = promotionMethodCallType.GetConstructors()[0]; + + return constructor.Invoke(new[] { introduceOptimizationExpr }); + } + + public IOptimizableEnumerableExpression OptimizeExpression(IOptimizableEnumerableExpression expression) + { + + + if(expression.PrevExpression != null) + return expression.AsOptimized2(this); + return expression; + } + + public IEnumerableExpression Optimize(ISQO expression) + { + if (expression is IOptimizableEnumerableExpression opt && expression.OptSource is IOptimizableEnumerableExpression optSource) + { + optSource.PrevExpression = opt.OptSelectorExpression; + var test = optSource.AsOptimized2(this); + return Optimize(test); + } + + return (IEnumerableExpression)expression; + } + + public IOptimizableEnumerableExpression OptimizeExpression(SelectExpression expression) + { + var mergedSelectorExpression = this.Optimize(expression.PrevExpression, expression.SelectorExpression) as Expression>; + return new SelectExpression(expression.Source, mergedSelectorExpression, null); + } + + public IOptimizableEnumerableExpression OptimizeExpression(SelectManyExpression expression) + { + if (expression.PrevExpression == null) + throw new ArgumentNullException("expression.PrevExpression"); + + var mergedSelectorExpression = this.Optimize(expression.PrevExpression,expression.ResultSelector) as Expression>; + return new SelectManyExpression(expression.Source, expression.FuncExpression, null, mergedSelectorExpression, null); + } + + public IOptimizableEnumerableExpression OptimizeExpression(JoinExpression expression) + { + if (expression.PrevExpression == null) + throw new ArgumentNullException("expression.PrevExpression"); + + var mergedSelectorExpression = this.Optimize(expression.PrevExpression, expression.ResultSelector) as Expression>; + return new JoinExpression(expression.Source, expression.Inner, expression.OuterKeySelector, null, expression.InnerKeySelector, null, mergedSelectorExpression, null, expression.Comparer); + } + + public IOptimizableEnumerableExpression OptimizeExpression(GroupJoinExpression expression) + { + if (expression.PrevExpression == null) + throw new ArgumentNullException("expression.PrevExpression"); + + var mergedSelectorExpression = this.Optimize(expression.PrevExpression, expression.ResultSelector) as Expression, TOptimizedResult>>; + return new GroupJoinExpression(expression.Source, expression.Inner, expression.OuterKeySelector, null, expression.InnerKeySelector, null, mergedSelectorExpression, null, expression.Comparer); + } + + } +} \ No newline at end of file diff --git a/Expressions/Expressions.Linq/ExpressionsOptimization/QueryOptimizer.cs b/Expressions/Expressions.Linq/ExpressionsOptimization/QueryOptimizer.cs new file mode 100644 index 00000000..bd6db6bd --- /dev/null +++ b/Expressions/Expressions.Linq/ExpressionsOptimization/QueryOptimizer.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +namespace NMF.Expressions +{ + /// + /// This class encapsulates a default query optimizer + /// + static class QueryOptimizer + { + private static IQueryOptimizer defaultQueryOptimizer = new ProjectionMergeQueryOptimizer(); + + /// + /// Gets or sets the incremental computation system to be used by default + /// + /// This property can never be set to a null value + public static IQueryOptimizer DefaultQueryOptimizer + { + get + { + return defaultQueryOptimizer; + } + set + { + defaultQueryOptimizer = value ?? defaultQueryOptimizer; + } + } + } +} \ No newline at end of file diff --git a/Expressions/Expressions.Linq/ExpressionsOptimization/QueryOptimizerVisitor.cs b/Expressions/Expressions.Linq/ExpressionsOptimization/QueryOptimizerVisitor.cs new file mode 100644 index 00000000..02df6cb4 --- /dev/null +++ b/Expressions/Expressions.Linq/ExpressionsOptimization/QueryOptimizerVisitor.cs @@ -0,0 +1,37 @@ +using System.Linq; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; + +namespace NMF.Expressions +{ + public class QueryOptimizerVisitor: ExpressionVisitorBase + { + private readonly LambdaExpression _firstLambdaSelectorExpression; + private readonly LambdaExpression _secondLambdaSelectorExpression; + + public ParameterExpression OptimizationVaribable { get; set; } + + public QueryOptimizerVisitor(LambdaExpression firstLambdaSelectorExpression, LambdaExpression secondLambdaSelectorExpression) + { + _firstLambdaSelectorExpression = firstLambdaSelectorExpression; + _secondLambdaSelectorExpression = secondLambdaSelectorExpression; + } + + protected override Expression VisitParameter(ParameterExpression node) + { + + var param = _firstLambdaSelectorExpression.Parameters[0]; + + if (node.ToString().Equals(param.ToString())) + { + if(OptimizationVaribable != null) + return base.VisitParameter(OptimizationVaribable); + + //anstelle des Parameters, die zweite LambdaExpression einfügen + return _secondLambdaSelectorExpression.Body; + } + return base.VisitParameter(node); + } + + } +} \ No newline at end of file diff --git a/Expressions/Expressions.Linq/Facade/IEnumerableExpression.cs b/Expressions/Expressions.Linq/Facade/IEnumerableExpression.cs index 6399c82b..c18569bd 100644 --- a/Expressions/Expressions.Linq/Facade/IEnumerableExpression.cs +++ b/Expressions/Expressions.Linq/Facade/IEnumerableExpression.cs @@ -1,9 +1,66 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; +using System.Text; namespace NMF.Expressions { + internal interface ISQO + { + IEnumerableExpression OptSource { get; } + } + + internal interface IOptimizableEnumerableExpression : ISQO + { + Expression OptSelectorExpression { get; } + + Expression PrevExpression { set; get; } + + /// + /// Gets optimization for this collection + /// + /// + /// + /// + IEnumerableExpression AsOptimized(IOptimizableEnumerableExpression prevOptExpression = null); + + IEnumerableExpression Merge(IOptimizableEnumerableExpression prevOptExpression); + + IOptimizableEnumerableExpression AsOptimized2(IQueryOptimizer queryOptimizer); + } + + /// + /// Represents a collection that can be optimized + /// + internal interface IOptimizableEnumerableExpression : IOptimizableEnumerableExpression + { + /// + /// Gets optimization for this collection + /// + /// + /// + /// + //IEnumerableExpression AsOptimized(IOptimizableEnumerableExpression prevOptExpression = null); + + //IEnumerableExpression Merge(IOptimizableEnumerableExpression prevOptExpression); + } + + internal interface IOptimizableEnumerableExpression : IOptimizableEnumerableExpression + { + new IOptimizableEnumerableExpression AsOptimized2(IQueryOptimizer queryOptimizer); + } + + internal interface IOptimizableEnumerableExpression : IOptimizableEnumerableExpression + { + new IOptimizableEnumerableExpression AsOptimized2(IQueryOptimizer queryOptimizer); + } + + internal interface IOptimizableEnumerableExpression : IOptimizableEnumerableExpression + { + new IOptimizableEnumerableExpression AsOptimized2(IQueryOptimizer queryOptimizer); + } + /// /// Represents a collection that can be accessed incrementally, i.e. with change notifications /// diff --git a/Expressions/Expressions.history b/Expressions/Expressions.history index 74bd5132..78ccb90f 100644 --- a/Expressions/Expressions.history +++ b/Expressions/Expressions.history @@ -21,3 +21,4 @@ patch (2.0.457): Start using EtiCat minor: introduce flag to indicate whether collections define an order minor: introduce parameter to control how where removes elements patch: Add support for .NET 10 +minor: Support query optimizations diff --git a/Expressions/Tests/Models.Expressions.Tests/NotifySystemTests.cs b/Expressions/Tests/Models.Expressions.Tests/NotifySystemTests.cs index 6fbe78b1..b81f1362 100644 --- a/Expressions/Tests/Models.Expressions.Tests/NotifySystemTests.cs +++ b/Expressions/Tests/Models.Expressions.Tests/NotifySystemTests.cs @@ -7,6 +7,7 @@ using System.CodeDom.Compiler; using System.Collections.Specialized; using System.Linq; +using System.Text; namespace NMF.Expressions.Tests { @@ -263,29 +264,6 @@ public void NotifySystem_CheckSwitchPositionSensor() Assert.IsFalse(test.Value); Assert.IsFalse(resultChanged); - - // For this to work, we would need to execute the model changes in a transaction - //var originalSwitch = switchPosition.Switch; - //var newSwitch = route2.DefinedBy[0].Elements.OfType().FirstOrDefault(); - //switchPosition.Switch = newSwitch; - - //Assert.IsTrue(resultChanged); - //Assert.IsTrue(test.Value); - - //route.DefinedBy.Remove(originalSwitch.Sensor); - - //Assert.IsFalse(resultChanged); - - //route.DefinedBy.Add(newSwitch.Sensor); - - //resultChanged = false; - //expectedOld = true; - //expectedNew = false; - - //switchPosition.Switch.Sensor = null; - - //Assert.IsTrue(resultChanged); - //Assert.IsFalse(test.Value); } [TestMethod] @@ -460,6 +438,123 @@ from te2 in te1.ConnectsTo Assert.AreEqual(func.Count(), incremental.Count()); } + private IEnumerableExpression CreateSelectManySelectManyExpressionOptimizable() + { + var func = CreateExpression( + from route in RailwayContainer.Routes + where route.Entry != null && route.Entry.Signal == Signal.GO + from swP in route.Follows.OfType() + from sensor in route.DefinedBy + select swP.Position.ToString() + sensor.Id.ToString()); + + var test = func.AsNotifiable(); + + + return func; + } + + private IEnumerableExpression CreateJoinSelectExpressionOptimizable() + { + var func = CreateExpression( + from route in RailwayContainer.Routes + where route.Entry != null && route.Entry.Signal == Signal.GO + from swP in route.Follows.OfType() + let routeString = route.ToString() + select swP.Position.ToString()); + + + var test = func.AsNotifiable(); + + return func; + } + + [TestMethod] + public void NotifySystem_Optimize_SelectSelect() + { + var func = CreateExpression(from route in RailwayContainer.Routes + where route.Entry != null && route.Entry.Signal == Signal.GO + let routeString = route.ToString() + select route.ToIdentifierString() + routeString + routeString); + + + var routeTest = RailwayContainer.Routes[0]; + + var test = func.AsNotifiable(); + + var resultChanged = false; + test.CollectionChanged += (o, e) => + { + resultChanged = true; + }; + + Assert.AreEqual(func.Count(), test.Count()); + Assert.IsFalse(resultChanged); + + + routeTest.Entry.Signal = Signal.STOP; + + Assert.IsTrue(resultChanged); + Assert.AreEqual(func.Count(), test.Count()); + } + + [TestMethod] + public void NotifySystem_Optimize_SelectSelectSelect() + { + var func = CreateExpression( + from route in RailwayContainer.Routes + where route.Entry != null && route.Entry.Signal == Signal.GO + let routeEntry = route.Entry.ToString() + let routeString = route.ToString() + select routeEntry + routeString); + + var routeTest = RailwayContainer.Routes[0]; + + var test = func.AsNotifiable(); + + var resultChanged = false; + test.CollectionChanged += (o, e) => + { + resultChanged = true; + }; + + Assert.AreEqual(func.Count(), test.Count()); + Assert.IsFalse(resultChanged); + + routeTest.Entry = new Semaphore(); + + Assert.IsTrue(resultChanged); + Assert.AreEqual(func.Count(), test.Count()); + } + + [TestMethod] + public void NotifySystem_Optimize_SelectManySelect() + { + var func = CreateExpression( + from route in RailwayContainer.Routes + where route.Entry != null && route.Entry.Signal == Signal.GO + from swP in route.Follows.OfType() + let routeString = route.ToString() + select swP.Position.ToString()); + + var routeTest = RailwayContainer.Routes[0]; + + var test = func.AsNotifiable(); + + var resultChanged = false; + test.CollectionChanged += (o, e) => + { + resultChanged = true; + }; + + Assert.AreEqual(func.Count(), test.Count()); + Assert.IsFalse(resultChanged); + + routeTest.Entry = new Semaphore(); + + Assert.IsTrue(resultChanged); + Assert.AreEqual(func.Count(), test.Count()); + } + private IEnumerableExpression CreateExpression(IEnumerableExpression value) { return value;