Add project files.
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
using Elsa.Sql.Client;
|
||||
|
||||
namespace Elsa.Sql.Services;
|
||||
public class ClientStore
|
||||
{
|
||||
private readonly Dictionary<string, Type> clients = new();
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary of registered clients and their type.
|
||||
/// </summary>
|
||||
public IReadOnlyDictionary<string, Type> Clients => clients;
|
||||
|
||||
/// <summary>
|
||||
/// Registers the specified client type <typeparamref name="TClient"/> with the store.
|
||||
/// The client type must inherit from <see cref="ISqlClient"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TClient">
|
||||
/// The type of the client to be registered. The client must be a class that implements the <see cref="ISqlClient"/> interface.
|
||||
/// </typeparam>
|
||||
/// <param name="name">
|
||||
/// The name of the client to register. If not provided, the name defaults to <c>nameof(TClient)</c>.
|
||||
/// This value is used as a key to identify the client in the store.
|
||||
/// </param>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown when a client with the same name is already registered in the store.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// This method registers a client type to the store using a unique key. The key is either the provided <paramref name="name"/> or the default name derived from <typeparamref name="TClient"/>.
|
||||
/// </remarks>
|
||||
public void Register<TClient>(string? name) where TClient : class, ISqlClient
|
||||
{
|
||||
var key = string.IsNullOrEmpty(name) ? nameof(TClient) : name;
|
||||
if (clients.ContainsKey(key)) { throw new InvalidOperationException($"Client with key '{name}' is already registered."); }
|
||||
clients.Add(key, typeof(TClient));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
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}}}}}.")
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user