using System.Text.RegularExpressions; using SqlSugar; namespace Apq.Cfg.Database; /// /// 基于 SqlSugar 的统一数据库配置提供程序 /// internal sealed partial class SqlSugarDatabaseProvider { private readonly DbType _dbType; #if NET7_0_OR_GREATER [GeneratedRegex(@"^[a-zA-Z_][a-zA-Z0-9_]*$", RegexOptions.Compiled)] private static partial Regex SafeIdentifierRegex(); #else private static readonly Regex _safeIdentifierRegex = new(@"^[a-zA-Z_][a-zA-Z0-9_]*$", RegexOptions.Compiled); private static Regex SafeIdentifierRegex() => _safeIdentifierRegex; #endif public SqlSugarDatabaseProvider(DbType dbType) { _dbType = dbType; } private static void ValidateIdentifier(string identifier, string paramName) { if (string.IsNullOrWhiteSpace(identifier)) throw new ArgumentException($"标识符 '{paramName}' 不能为空", paramName); if (identifier.Length > 128) throw new ArgumentException($"标识符 '{paramName}' 长度不能超过 128 个字符", paramName); if (!SafeIdentifierRegex().IsMatch(identifier)) throw new ArgumentException( $"标识符 '{paramName}' 包含非法字符。只允许字母、数字和下划线,且不能以数字开头。值: '{identifier}'", paramName); } public async Task> LoadConfigurationAsync( string connectionString, string tableName, string keyColumn, string valueColumn, CancellationToken cancellationToken = default) { ValidateIdentifier(tableName, nameof(tableName)); ValidateIdentifier(keyColumn, nameof(keyColumn)); ValidateIdentifier(valueColumn, nameof(valueColumn)); var result = new Dictionary(); using var db = CreateDb(connectionString); var query = $"SELECT {Quote(keyColumn)}, {Quote(valueColumn)} FROM {Quote(tableName)}"; var dataTable = await Task.Run(() => db.Ado.GetDataTable(query), cancellationToken); foreach (System.Data.DataRow row in dataTable.Rows) { var key = row[keyColumn]?.ToString(); if (key != null) { var value = row[valueColumn] == DBNull.Value ? null : row[valueColumn]?.ToString(); result[key] = value; } } return result; } public async Task ApplyChangesAsync( string connectionString, string tableName, string keyColumn, string valueColumn, IReadOnlyDictionary changes, CancellationToken cancellationToken = default) { ValidateIdentifier(tableName, nameof(tableName)); ValidateIdentifier(keyColumn, nameof(keyColumn)); ValidateIdentifier(valueColumn, nameof(valueColumn)); using var db = CreateDb(connectionString); await Task.Run(() => { // 确保表存在 EnsureTableExists(db, tableName, keyColumn, valueColumn); db.Ado.BeginTran(); try { foreach (var (key, value) in changes) { var existsQuery = $"SELECT COUNT(1) FROM {Quote(tableName)} WHERE {Quote(keyColumn)} = @key"; var exists = db.Ado.GetInt(existsQuery, new { key }) > 0; if (exists) { var updateQuery = $"UPDATE {Quote(tableName)} SET {Quote(valueColumn)} = @value WHERE {Quote(keyColumn)} = @key"; db.Ado.ExecuteCommand(updateQuery, new { key, value }); } else { var insertQuery = $"INSERT INTO {Quote(tableName)} ({Quote(keyColumn)}, {Quote(valueColumn)}) VALUES (@key, @value)"; db.Ado.ExecuteCommand(insertQuery, new { key, value }); } } db.Ado.CommitTran(); } catch { db.Ado.RollbackTran(); throw; } }, cancellationToken); } private SqlSugarClient CreateDb(string connectionString) { return new SqlSugarClient(new ConnectionConfig { ConnectionString = connectionString, DbType = _dbType, IsAutoCloseConnection = false }); } private void EnsureTableExists(SqlSugarClient db, string tableName, string keyColumn, string valueColumn) { var createSql = _dbType switch { DbType.SqlServer => $@" IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = '{tableName}') CREATE TABLE [{tableName}] ( [{keyColumn}] NVARCHAR(512) NOT NULL PRIMARY KEY, [{valueColumn}] NVARCHAR(MAX) NULL )", DbType.MySql => $@" CREATE TABLE IF NOT EXISTS `{tableName}` ( `{keyColumn}` VARCHAR(512) NOT NULL PRIMARY KEY, `{valueColumn}` TEXT NULL )", DbType.PostgreSQL => $@" CREATE TABLE IF NOT EXISTS ""{tableName}"" ( ""{keyColumn}"" VARCHAR(512) NOT NULL PRIMARY KEY, ""{valueColumn}"" TEXT NULL )", DbType.Sqlite => $@" CREATE TABLE IF NOT EXISTS ""{tableName}"" ( ""{keyColumn}"" TEXT NOT NULL PRIMARY KEY, ""{valueColumn}"" TEXT NULL )", DbType.Oracle => $@" BEGIN EXECUTE IMMEDIATE 'CREATE TABLE ""{tableName.ToUpperInvariant()}"" ( ""{keyColumn.ToUpperInvariant()}"" VARCHAR2(512) NOT NULL PRIMARY KEY, ""{valueColumn.ToUpperInvariant()}"" CLOB NULL )'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -955 THEN RAISE; END IF; END;", _ => throw new NotSupportedException($"不支持的数据库类型: {_dbType}") }; db.Ado.ExecuteCommand(createSql); } private string Quote(string identifier) { return _dbType switch { DbType.SqlServer => $"[{identifier}]", DbType.MySql => $"`{identifier}`", DbType.PostgreSQL => $"\"{identifier}\"", DbType.Oracle => $"\"{identifier.ToUpperInvariant()}\"", DbType.Sqlite => $"\"{identifier}\"", _ => identifier }; } }