Add ReplacePlaceholders for SQL-style string interpolation

Introduce ReplacePlaceholders to PlaceholderExtensions, enabling replacement of {#...#COLUMN_NAME} placeholders in strings with property values from provided objects. Uses a generated regex for matching and converts values to SQL-compatible literals. Throws PlaceholderResolutionException if a column cannot be resolved. Refactored class to partial to support regex generation.
This commit is contained in:
2026-03-26 14:37:29 +01:00
parent 7a11ac3635
commit c2e073dade

View File

@@ -1,10 +1,51 @@
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Reflection; using System.Reflection;
using System.Text.RegularExpressions;
using ReC.Application.Common.Exceptions;
namespace ReC.Application.Common.Behaviors.InvokeAction; namespace ReC.Application.Common.Behaviors.InvokeAction;
public static class PlaceholderExtensions public static partial class PlaceholderExtensions
{ {
[GeneratedRegex(@"\{#[^#]+#[^}]+\}")]
private static partial Regex PlaceholderRegex();
/// <summary>
/// Replaces placeholders in the format <c>{#ANY_STRING#COLUMN_NAME}</c> with the corresponding
/// property value resolved via <see cref="GetValueByColumnName{T}"/> from the provided objects.
/// Values are converted to SQL-compatible string representations.
/// </summary>
/// <exception cref="PlaceholderResolutionException">
/// Thrown when a placeholder's column name cannot be resolved from any of the provided objects.
/// </exception>
public static string ReplacePlaceholders(this string str, params object[] objects)
{
return PlaceholderRegex().Replace(str, match =>
{
var placeholder = match.Value;
var inner = placeholder[2..^1]; // remove {# and }
var lastHash = inner.LastIndexOf('#');
var columnName = inner[(lastHash + 1)..];
foreach (var obj in objects)
{
var value = obj.GetValueByColumnName(columnName);
if (value is not null)
return ToSqlLiteral(value);
}
throw new PlaceholderResolutionException(placeholder, columnName, str);
});
}
private static string ToSqlLiteral(object value) => value switch
{
bool b => b ? "TRUE" : "FALSE",
DateTime dt => dt.ToString("yyyy-MM-dd HH:mm:ss"),
DateTimeOffset dto => dto.ToString("yyyy-MM-dd HH:mm:ss zzz"),
_ => value.ToString() ?? string.Empty
};
/// <summary> /// <summary>
/// Gets the value of a property by its column name defined in <see cref="ColumnAttribute"/>. /// Gets the value of a property by its column name defined in <see cref="ColumnAttribute"/>.
/// Returns <c>null</c> if no property with the given column name exists. /// Returns <c>null</c> if no property with the given column name exists.