using System.Data; using System.Globalization; namespace Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess; /// /// S8 监控数据的 canonical 单行。 /// /// 设计约束(与迁移前 DataRow 读取语义严格一致,不得"顺手改进"): /// · 取值器缺列返回 null,不抛异常 —— evaluator 依赖此行为做 canonical→params 回退; /// · DBNull 与 null 等价,均返回 null;**绝不用字符串 "null" 表示空值**; /// · GetString 去首尾空白;GetDecimal / GetDateTime / GetLong 支持字符串与强类型两种来源 /// (Legacy SQL 走 DataTable 常给字符串,标准 Provider 常给强类型); /// · 保留结果集全部原始列,payload 落盘依赖它。 /// /// 列名比较一律 OrdinalIgnoreCase,与既有 payload 字典(StringComparer.OrdinalIgnoreCase)保持一致。 /// public sealed class S8MonitoringRow { private readonly Dictionary _values; private S8MonitoringRow(Dictionary values) => _values = values; /// 结果集原始列 → 值。缺失值为 null(不是 DBNull,不是 "null" 字符串)。 public IReadOnlyDictionary Values => _values; /// 由列值字典构造一行。标准 Provider 使用此入口。 public static S8MonitoringRow FromValues(IReadOnlyDictionary values) { var copy = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var kv in values) copy[kv.Key] = kv.Value == DBNull.Value ? null : kv.Value; return new S8MonitoringRow(copy); } internal static S8MonitoringRow FromDataRow(DataRow row) { var values = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (DataColumn column in row.Table.Columns) { var v = row[column]; values[column.ColumnName] = v == DBNull.Value ? null : v; } return new S8MonitoringRow(values); } /// 列是否存在于本行。 public bool HasColumn(string? columnName) => !string.IsNullOrWhiteSpace(columnName) && _values.ContainsKey(columnName!); private bool TryGetRaw(string? columnName, out object? value) { value = null; if (string.IsNullOrWhiteSpace(columnName)) return false; if (!_values.TryGetValue(columnName!, out var v)) return false; if (v is null) return false; value = v; return true; } /// 读字符串并去首尾空白;缺列 / 空值返回 null。 public string? GetString(string? columnName) => TryGetRaw(columnName, out var v) ? Convert.ToString(v)?.Trim() : null; /// 读时间;支持 DateTime 与可解析字符串。缺列 / 空值 / 无法解析返回 null。 public DateTime? GetDateTime(string? columnName) { if (!TryGetRaw(columnName, out var v)) return null; if (v is DateTime dt) return dt; return DateTime.TryParse(Convert.ToString(v, CultureInfo.InvariantCulture), out var parsed) ? parsed : null; } /// 读小数;支持数值与可解析字符串。缺列 / 空值 / 无法解析返回 null。 public decimal? GetDecimal(string? columnName) { if (!TryGetRaw(columnName, out var v)) return null; if (v is decimal d) return d; return decimal.TryParse( Convert.ToString(v, CultureInfo.InvariantCulture), NumberStyles.Any, CultureInfo.InvariantCulture, out var parsed) ? parsed : null; } /// 读长整型。缺列 / 空值 / 无法解析返回 null。 public long? GetLong(string? columnName) { if (!TryGetRaw(columnName, out var v)) return null; if (v is long l) return l; return long.TryParse(Convert.ToString(v, CultureInfo.InvariantCulture), out var parsed) ? parsed : null; } }