using Connected.Entities.Storage; using Connected.Expressions.Expressions; using Connected.Expressions.Languages; using Connected.Expressions.Visitors; using Connected.Interop; using System.Data; using System.Linq.Expressions; namespace Connected.Expressions.Evaluation; public sealed class ExecutionBuilder : DatabaseVisitor { private ExecutionBuilder(ExpressionCompilationContext context, Linguist linguist, Expression executor) { Context = context; Linguist = linguist; Executor = executor; } private ExpressionCompilationContext Context { get; } private Linguist Linguist { get; } private Expression Executor { get; } public static Expression> Build(ExpressionCompilationContext context, Linguist linguist, Expression expression, Expression provider) { var executor = Expression.Parameter(typeof(IStorageExecutor), "executor"); var builder = new ExecutionBuilder(context, linguist, executor); return builder.Build(expression); } private Expression> Build(Expression expression) { expression = Visit(expression); expression = Expression.Lambda>(expression, (ParameterExpression)Executor); return (Expression>)expression; } protected override Expression VisitProjection(ProjectionExpression projection) { /* * parameterize query */ var commandText = Linguist.Format(projection.Select); var command = new StorageOperation { CommandText = commandText }; foreach (var parameter in Context.Parameters) { command.AddParameter(new StorageParameter { Direction = ParameterDirection.Input, Name = $"@{parameter.Key}", Type = parameter.Value.Type.ToDbType(), Value = parameter.Value.Value }); }; return ExecuteProjection(projection, command); } private Expression ExecuteProjection(ProjectionExpression projection, IStorageOperation operation) { var method = nameof(IStorageExecutor.Execute); /* * call low-level execute directly on supplied DbQueryProvider */ Type typeArgument; if (projection.Type.IsEnumerable()) typeArgument = projection.Type.GetGenericArguments()[0]; else typeArgument = projection.Type; var constant = Expression.Constant(operation); Expression body = Expression.Call(Executor, nameof(IStorageExecutor.Execute), new Type[] { typeArgument }, constant); if (projection.Aggregator is not null)// apply aggregator body = ExpressionReplacer.Replace(projection.Aggregator.Body, projection.Aggregator.Parameters[0], body); return body; } }