You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Connected.Framework/Connected.Expressions/Evaluation/ExecutionBuilder.cs

87 lines
2.9 KiB

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<Func<IStorageExecutor, object>> 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<Func<IStorageExecutor, object>> Build(Expression expression)
{
expression = Visit(expression);
expression = Expression.Lambda<Func<IStorageExecutor, object>>(expression, (ParameterExpression)Executor);
return (Expression<Func<IStorageExecutor, object>>)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;
}
}