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;
}
}