Files
Caspian.Elsa.Core/src/modules/Elsa.Sql/Services/SqlEvaluator.cs
T
2025-08-18 17:11:43 +03:30

94 lines
3.9 KiB
C#

using System.Text;
using Elsa.Expressions.Models;
using Elsa.Extensions;
using Elsa.Sql.Contracts;
using Elsa.Sql.Models;
using Elsa.Workflows;
namespace Elsa.Sql.Services;
/// <summary>
/// A SQL expression evaluator.
/// </summary>
/// <remarks>
/// Initializes a new instance of the <see cref="SqlEvaluator"/> class.WS
/// </remarks>
public class SqlEvaluator() : ISqlEvaluator
{
private WorkflowExecutionContext executionContext;
private ActivityExecutionContext activityContext;
private ExpressionExecutionContext expressionContext;
/// <inheritdoc />
public async Task<EvaluatedQuery> EvaluateAsync(
string expression,
ExpressionExecutionContext context,
ExpressionEvaluatorOptions options,
CancellationToken cancellationToken = default)
{
if (!expression.Contains("{{")) return new EvaluatedQuery(expression);
expressionContext = context;
executionContext = context.GetWorkflowExecutionContext();
activityContext = context.GetActivityExecutionContext();
// Create client
var factory = context.GetRequiredService<ISqlClientFactory>();
var client = factory.CreateClient(activityContext.ActivityState["Client"].ToString(), activityContext.ActivityState["ConnectionString"].ToString());
var sb = new StringBuilder();
int start = 0;
var parameters = new Dictionary<string, object?>();
int paramIndex = 0;
while (start < expression.Length)
{
int openIndex = expression.IndexOf("{{", start);
if (openIndex == -1)
{
sb.Append(expression.AsSpan(start));
break;
}
// Append everything before {{
sb.Append(expression.AsSpan(start, openIndex - start));
// Find the closing }}
int closeIndex = expression.IndexOf("}}", openIndex + 2);
if (closeIndex == -1) throw new FormatException("Unmatched '{{' found in SQL expression.");
// Extract key
string key = expression.Substring(openIndex + 2, closeIndex - openIndex - 2).Trim();
if (string.IsNullOrEmpty(key)) throw new FormatException("Empty placeholder '{{}}' is not allowed.");
// Resolve value and replace with parameterized name
var counterValue = client.IncrementParameter ? paramIndex++.ToString() : string.Empty;
string paramName = $"{client.ParameterMarker}{client.ParameterText}{counterValue}";
parameters[paramName] = ResolveValue(key);
sb.Append(paramName);
start = closeIndex + 2;
}
return new EvaluatedQuery(sb.ToString(), parameters);
}
private object? ResolveValue(string key)
{
return key switch
{
"Workflow.Definition.Id" => executionContext.Workflow.Identity.DefinitionId,
"Workflow.Definition.Version.Id" => executionContext.Workflow.Identity.Id,
"Workflow.Definition.Version" => executionContext.Workflow.Identity.Version,
"Workflow.Instance.Id" => activityContext.WorkflowExecutionContext.Id,
"Correlation.Id" => activityContext.WorkflowExecutionContext.CorrelationId,
"LastResult" => expressionContext.GetLastResult(),
var i when i.StartsWith("Input.") => executionContext.Input.TryGetValue(i.Substring(6), out var v) ? v : null,
var o when o.StartsWith("Output.") => executionContext.Output.TryGetValue(o.Substring(7), out var v) ? v : null,
var v when v.StartsWith("Variable.") => expressionContext.GetVariableInScope(v.Substring(9)) ?? null,
// OBSOLETE: This is deprecated and will be removed in a future version. Use 'Variable.' instead.
var v when v.StartsWith("Variables.") => expressionContext.GetVariableInScope(v.Substring(10)) ?? null,
_ => throw new NullReferenceException($"No matching property found for {{{{{key}}}}}.")
};
}
}