| | | 1 | | using System.Text.RegularExpressions; |
| | | 2 | | |
| | | 3 | | namespace Services.BitrixStockServices; |
| | | 4 | | |
| | | 5 | | /// <summary> |
| | | 6 | | /// Защита от передачи <see cref="CancellationToken"/> в params object[] ExecuteSqlRawAsync |
| | | 7 | | /// (попадает в SQL как {N} и ломает EF: "store type mapping for CancellationToken"). |
| | | 8 | | /// </summary> |
| | | 9 | | public static class EfSqlRawParameterGuard |
| | | 10 | | { |
| | 1 | 11 | | private static readonly Regex PlaceholderRegex = new(@"\{(\d+)\}", RegexOptions.Compiled); |
| | | 12 | | |
| | | 13 | | public static void Validate(string sql, IReadOnlyList<object?> parameters, string checkpoint) |
| | | 14 | | { |
| | 238 | 15 | | for (var i = 0; i < parameters.Count; i++) |
| | | 16 | | { |
| | 110 | 17 | | if (parameters[i] is CancellationToken) |
| | 7 | 18 | | throw new InvalidOperationException( |
| | 7 | 19 | | $"[{checkpoint}] CancellationToken на позиции {i} в SQL-параметрах — используйте перегрузку ExecuteS |
| | | 20 | | } |
| | | 21 | | |
| | 9 | 22 | | var maxIndex = PlaceholderRegex.Matches(sql) |
| | 64 | 23 | | .Select(m => int.Parse(m.Groups[1].Value)) |
| | 9 | 24 | | .DefaultIfEmpty(-1) |
| | 9 | 25 | | .Max(); |
| | | 26 | | |
| | 9 | 27 | | if (maxIndex >= 0 && parameters.Count <= maxIndex) |
| | 2 | 28 | | throw new InvalidOperationException( |
| | 2 | 29 | | $"[{checkpoint}] Ожидается минимум {maxIndex + 1} SQL-параметров, передано {parameters.Count}"); |
| | 7 | 30 | | } |
| | | 31 | | |
| | 0 | 32 | | public static object[] Materialize(params object?[] parameters) => Materialize((IReadOnlyList<object?>)parameters); |
| | | 33 | | |
| | | 34 | | /// <summary>EF ExecuteSqlRawAsync: object?[] → object[]. null остаётся null — EF Core 8 + Pomelo обрабатывает его к |
| | | 35 | | public static object[] Materialize(IReadOnlyList<object?> parameters) |
| | | 36 | | { |
| | 0 | 37 | | var result = new object[parameters.Count]; |
| | 0 | 38 | | for (var i = 0; i < parameters.Count; i++) |
| | 0 | 39 | | result[i] = parameters[i]!; |
| | 0 | 40 | | return result; |
| | | 41 | | } |
| | | 42 | | } |