Files
ReC/src/ReC.Application/Common/Dto/PlaceholderExtensions.cs
TekH bb5eac023c Change ReplacePlaceholders to return NULL for unresolved
Previously, ReplacePlaceholders threw PlaceholderResolutionException when a placeholder could not be resolved. Now, unresolved placeholders are replaced with "NULL" instead. All exception references and related tests have been updated to reflect this new behavior. Documentation has also been revised accordingly.
2026-03-30 14:33:13 +02:00

61 lines
2.2 KiB
C#

using System.ComponentModel.DataAnnotations.Schema;
using System.Reflection;
using System.Text.RegularExpressions;
namespace ReC.Application.Common.Dto;
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.
/// If a placeholder cannot be resolved, it is replaced with <c>NULL</c>.
/// </summary>
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)
{
if (obj is null)
continue;
var value = obj.GetValueByColumnName(columnName);
if (value is not null)
return ToSqlLiteral(value);
}
return "NULL";
});
}
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>
/// 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.
/// </summary>
public static object? GetValueByColumnName<T>(this T obj, string columnName) where T : class
{
var property = obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
.FirstOrDefault(p => p.GetCustomAttribute<ColumnAttribute>()?.Name == columnName);
return property?.GetValue(obj);
}
}