Initial_commit_SecMPS_v2
This commit is contained in:
@@ -0,0 +1,558 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Builder.IServices;
|
||||
using VolPro.Core.EFDbContext;
|
||||
namespace VolPro.Builder.Services;
|
||||
|
||||
public class SqlServerTableProvider : ITableDatabaseProvider
|
||||
{
|
||||
private readonly BaseDbContext _context;
|
||||
|
||||
public SqlServerTableProvider(BaseDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
private static string NormalizeSqlServerDefault(string raw)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(raw)) return null;
|
||||
var s = raw.Trim();
|
||||
while (s.StartsWith("(") && s.EndsWith(")")) s = s[1..^1].Trim();
|
||||
return string.IsNullOrWhiteSpace(s) ? null : s;
|
||||
}
|
||||
|
||||
private static string FormatDefaultForSqlServer(string value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value)) return string.Empty;
|
||||
var v = value.Trim();
|
||||
var vLower = v.ToLower();
|
||||
if (vLower is "getdate()" or "newid()" or "sysdatetime()")
|
||||
return $" DEFAULT {v}";
|
||||
if (long.TryParse(v, out _) || decimal.TryParse(v, out _))
|
||||
return $" DEFAULT {v}";
|
||||
if (vLower == "true" || vLower == "1") return " DEFAULT 1";
|
||||
if (vLower == "false" || vLower == "0") return " DEFAULT 0";
|
||||
return $" DEFAULT N'{v.Replace("'", "''")}'";
|
||||
}
|
||||
|
||||
private static string BuildColumnDefinition(TableColumnDto column)
|
||||
{
|
||||
var definition = $"[{column.ColumnName}] ";
|
||||
var dtLower = (column.DataType ?? "").Trim().ToLower();
|
||||
if (dtLower == "nvarchar(max)") { dtLower = "nvarchar"; }
|
||||
if (dtLower == "varchar(max)") { dtLower = "varchar"; }
|
||||
if (dtLower is "nvarchar" or "varchar")
|
||||
{
|
||||
var length = (column.DataType ?? "").Contains("max", StringComparison.OrdinalIgnoreCase) ? -1 : (column.Length ?? 255);
|
||||
definition += $"{dtLower}({(length == -1 ? "MAX" : length.ToString())})";
|
||||
}
|
||||
else if (dtLower is "char" or "nchar")
|
||||
{
|
||||
var length = column.Length ?? 1;
|
||||
definition += $"{column.DataType}({length})";
|
||||
}
|
||||
else if (dtLower is "decimal" or "numeric")
|
||||
{
|
||||
var precision = column.Length ?? 18;
|
||||
var scale = column.Scale ?? 0;
|
||||
definition += $"{column.DataType}({precision}, {scale})";
|
||||
}
|
||||
else
|
||||
{
|
||||
definition += column.DataType ?? "nvarchar";
|
||||
}
|
||||
if (column.IsIdentity) definition += " IDENTITY(1,1)";
|
||||
if (!column.IsNullable) definition += " NOT NULL";
|
||||
definition += FormatDefaultForSqlServer(column.DefaultValue);
|
||||
return definition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL Server 不支持 ALTER COLUMN 修改 IDENTITY,通过临时表重建实现
|
||||
/// </summary>
|
||||
private async Task RebuildTableForIdentityChangeAsync(UpdateTableRequest request, Dictionary<string, TableColumnDto> existingByName)
|
||||
{
|
||||
var tableName = request.TableName;
|
||||
var tmpName = $"Tmp_{tableName}_{Guid.NewGuid().ToString("N")[..8]}";
|
||||
var colDefs = request.Columns.OrderBy(c => c.Order).Select(BuildColumnDefinition).ToList();
|
||||
var createTmpSql = $"CREATE TABLE [{tmpName}] (\n " + string.Join(",\n ", colDefs) + "\n)";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(createTmpSql);
|
||||
|
||||
var colsToCopy = request.Columns.Where(c => existingByName.ContainsKey(c.ColumnName)).OrderBy(c => c.Order).Select(c => c.ColumnName).ToList();
|
||||
var hasIdentity = request.Columns.Any(c => c.IsIdentity);
|
||||
var colList = string.Join(", ", colsToCopy.Select(c => $"[{c}]"));
|
||||
|
||||
// IDENTITY_INSERT 是会话级,必须与 INSERT 在同一连接/同一批中执行
|
||||
if (colsToCopy.Count > 0)
|
||||
{
|
||||
var insertBatch = hasIdentity
|
||||
? $"SET IDENTITY_INSERT [{tmpName}] ON; INSERT INTO [{tmpName}] ({colList}) SELECT {colList} FROM [{tableName}]; SET IDENTITY_INSERT [{tmpName}] OFF"
|
||||
: $"INSERT INTO [{tmpName}] ({colList}) SELECT {colList} FROM [{tableName}]";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(insertBatch);
|
||||
}
|
||||
|
||||
var dropPkSql = $@"
|
||||
DECLARE @pk NVARCHAR(200);
|
||||
SELECT @pk = name FROM sys.key_constraints WHERE type = 'PK' AND parent_object_id = OBJECT_ID('{tableName.Replace("'", "''")}');
|
||||
IF @pk IS NOT NULL EXEC('ALTER TABLE [{tableName}] DROP CONSTRAINT [' + @pk + ']')";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(dropPkSql);
|
||||
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync($"DROP TABLE [{tableName}]");
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync($"EXEC sp_rename 'dbo.[{tmpName}]', '{tableName.Replace("'", "''")}', 'OBJECT'");
|
||||
|
||||
if (request.Columns.Any(c => c.IsPrimaryKey))
|
||||
{
|
||||
var pkCols = request.Columns.Where(c => c.IsPrimaryKey).OrderBy(c => c.Order).Select(c => $"[{c.ColumnName}]");
|
||||
var addPkSql = $"ALTER TABLE [{tableName}] ADD CONSTRAINT [PK_{tableName}] PRIMARY KEY ({string.Join(", ", pkCols)})";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(addPkSql);
|
||||
}
|
||||
|
||||
foreach (var column in request.Columns.Where(c => !string.IsNullOrWhiteSpace(c.Comment)))
|
||||
{
|
||||
try
|
||||
{
|
||||
var commentSql = $@"
|
||||
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'{column.Comment.Replace("'", "''")}',
|
||||
@level0type = N'SCHEMA', @level0name = N'dbo', @level1type = N'TABLE', @level1name = N'{tableName.Replace("'", "''")}', @level2type = N'COLUMN', @level2name = N'{column.ColumnName.Replace("'", "''")}'";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(commentSql);
|
||||
}
|
||||
catch { /* 注释已存在时忽略 */ }
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> TableExistsAsync(string tableName)
|
||||
{
|
||||
const string sql = "SELECT COUNT(*) as Value FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = @tableName";
|
||||
var res = await _context.SqlSugarClient.Ado.GetScalarAsync(sql, new { tableName });
|
||||
return Convert.ToInt32(res) > 0;
|
||||
}
|
||||
|
||||
public async Task CreateTableAsync(CreateTableRequest request)
|
||||
{
|
||||
var columnDefinitions = new List<string>();
|
||||
|
||||
foreach (var column in request.Columns.OrderBy(c => c.Order))
|
||||
{
|
||||
var definition = $"[{column.ColumnName}] ";
|
||||
|
||||
// Data type and length
|
||||
var dtLower = column.DataType.ToLower();
|
||||
if (dtLower == "nvarchar" || dtLower == "varchar")
|
||||
{
|
||||
var length = column.Length ?? 255;
|
||||
definition += $"{column.DataType}({(length == -1 ? "MAX" : length.ToString())})";
|
||||
}
|
||||
else if (dtLower == "char" || dtLower == "nchar")
|
||||
{
|
||||
var length = column.Length ?? 1;
|
||||
definition += $"{column.DataType}({length})";
|
||||
}
|
||||
else if (dtLower == "decimal" || dtLower == "numeric")
|
||||
{
|
||||
var precision = column.Length ?? 18;
|
||||
var scale = column.Scale ?? 0;
|
||||
definition += $"{column.DataType}({precision}, {scale})";
|
||||
}
|
||||
else
|
||||
{
|
||||
definition += column.DataType;
|
||||
}
|
||||
|
||||
// Identity (Auto increment)
|
||||
if (column.IsIdentity)
|
||||
{
|
||||
definition += " IDENTITY(1,1)";
|
||||
}
|
||||
|
||||
// Nullable
|
||||
if (!column.IsNullable)
|
||||
{
|
||||
definition += " NOT NULL";
|
||||
}
|
||||
|
||||
// Default value
|
||||
definition += FormatDefaultForSqlServer(column.DefaultValue);
|
||||
|
||||
columnDefinitions.Add(definition);
|
||||
}
|
||||
|
||||
// Build CREATE TABLE statement
|
||||
var createTableSql = $"CREATE TABLE [{request.TableName}] (\n " +
|
||||
string.Join(",\n ", columnDefinitions) +
|
||||
"\n)";
|
||||
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(createTableSql);
|
||||
|
||||
// Add primary key constraint
|
||||
var pkColumns = request.Columns
|
||||
.Where(c => c.IsPrimaryKey)
|
||||
.OrderBy(c => c.Order)
|
||||
.Select(c => $"[{c.ColumnName}]");
|
||||
|
||||
var pkConstraintName = $"PK_{request.TableName}";
|
||||
var pkSql = $"ALTER TABLE [{request.TableName}] ADD CONSTRAINT [{pkConstraintName}] PRIMARY KEY ({string.Join(", ", pkColumns)})";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(pkSql);
|
||||
|
||||
// Add column comments (MS_Description)
|
||||
foreach (var column in request.Columns.Where(c => !string.IsNullOrWhiteSpace(c.Comment)))
|
||||
{
|
||||
var commentSql = $@"
|
||||
EXEC sp_addextendedproperty
|
||||
@name = N'MS_Description',
|
||||
@value = N'{column.Comment.Replace("'", "''")}',
|
||||
@level0type = N'SCHEMA', @level0name = N'dbo',
|
||||
@level1type = N'TABLE', @level1name = N'{request.TableName}',
|
||||
@level2type = N'COLUMN', @level2name = N'{column.ColumnName}'";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(commentSql);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<string>> GetAllTablesAsync()
|
||||
{
|
||||
const string sql = "SELECT TABLE_NAME as Value FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_NAME";
|
||||
var rows = await _context.SqlSugarClient.Ado.SqlQueryAsync<string>(sql);
|
||||
return rows ?? new List<string>();
|
||||
}
|
||||
|
||||
public async Task<TableInfoDto> GetTableInfoAsync(string tableName)
|
||||
{
|
||||
const string columnsSql = @"
|
||||
DECLARE @objId INT = OBJECT_ID(QUOTENAME('dbo') + '.' + QUOTENAME(@tableName));
|
||||
SELECT
|
||||
c.COLUMN_NAME,
|
||||
c.DATA_TYPE,
|
||||
c.CHARACTER_MAXIMUM_LENGTH,
|
||||
c.IS_NULLABLE,
|
||||
c.ORDINAL_POSITION,
|
||||
c.NUMERIC_SCALE,
|
||||
c.NUMERIC_PRECISION,
|
||||
COLUMNPROPERTY(@objId, c.COLUMN_NAME, 'IsIdentity') AS IS_IDENTITY,
|
||||
CASE WHEN pk.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END AS IS_PRIMARY_KEY,
|
||||
CAST(ep.value AS NVARCHAR(4000)) AS COMMENT,
|
||||
c.COLUMN_DEFAULT
|
||||
FROM INFORMATION_SCHEMA.COLUMNS c
|
||||
LEFT JOIN (
|
||||
SELECT ku.TABLE_NAME, ku.COLUMN_NAME
|
||||
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
|
||||
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE ku
|
||||
ON tc.CONSTRAINT_TYPE = 'PRIMARY KEY' AND tc.CONSTRAINT_NAME = ku.CONSTRAINT_NAME
|
||||
) pk ON c.TABLE_NAME = pk.TABLE_NAME AND c.COLUMN_NAME = pk.COLUMN_NAME
|
||||
LEFT JOIN sys.columns sc ON sc.object_id = @objId AND sc.name = c.COLUMN_NAME
|
||||
LEFT JOIN sys.extended_properties ep ON ep.major_id = sc.object_id AND ep.minor_id = sc.column_id AND ep.name = 'MS_Description'
|
||||
WHERE c.TABLE_SCHEMA = 'dbo' AND c.TABLE_NAME = @tableName
|
||||
ORDER BY c.ORDINAL_POSITION;";
|
||||
|
||||
var dt = await _context.SqlSugarClient.Ado.GetDataTableAsync(columnsSql, new { tableName });
|
||||
var columnsWithComments = new List<TableColumnDto>();
|
||||
foreach (DataRow row in dt.Rows)
|
||||
{
|
||||
var length = row[2] == DBNull.Value || row[2] == null ? (int?)null : Convert.ToInt32(row[2]);
|
||||
var numericScale = row[5] == DBNull.Value || row[5] == null ? (int?)null : Convert.ToInt32(row[5]);
|
||||
var numericPrecision = row[6] == DBNull.Value || row[6] == null ? (int?)null : Convert.ToInt32(row[6]);
|
||||
var dataType = row[1]?.ToString() ?? "";
|
||||
var isDecimal = dataType.Equals("decimal", StringComparison.OrdinalIgnoreCase) || dataType.Equals("numeric", StringComparison.OrdinalIgnoreCase);
|
||||
var colDefault = row[10] == DBNull.Value || row[10] == null ? null : NormalizeSqlServerDefault(row[10]?.ToString());
|
||||
|
||||
columnsWithComments.Add(new TableColumnDto
|
||||
{
|
||||
ColumnName = row[0]?.ToString() ?? "",
|
||||
DataType = dataType,
|
||||
Length = isDecimal ? numericPrecision : (length == -1 ? -1 : length),
|
||||
Scale = isDecimal ? numericScale : null,
|
||||
IsNullable = row[3]?.ToString() == "YES",
|
||||
Order = Convert.ToInt32(row[4]),
|
||||
IsIdentity = row[7] != DBNull.Value && row[7] != null && Convert.ToInt32(row[7]) == 1,
|
||||
IsPrimaryKey = row[8] != DBNull.Value && row[8] != null && Convert.ToInt32(row[8]) == 1,
|
||||
Comment = row[9]?.ToString(),
|
||||
DefaultValue = colDefault ?? string.Empty
|
||||
});
|
||||
}
|
||||
|
||||
return new TableInfoDto { TableName = tableName, Columns = columnsWithComments };
|
||||
}
|
||||
|
||||
public async Task UpdateTableAsync(UpdateTableRequest request)
|
||||
{
|
||||
var existingTable = await GetTableInfoAsync(request.TableName);
|
||||
if (existingTable == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Could not retrieve table information for '{request.TableName}'.");
|
||||
}
|
||||
|
||||
var existingByName = existingTable.Columns.ToDictionary(c => c.ColumnName, c => c, StringComparer.Ordinal);
|
||||
var newColumnNames = request.Columns.Select(c => c.ColumnName).ToHashSet(StringComparer.Ordinal);
|
||||
|
||||
// 1) 识别并执行重命名字段(区分大小写)
|
||||
var renames = request.Columns
|
||||
.Where(c => c.IsDbField && !string.IsNullOrWhiteSpace(c.OriginalColumnName) &&
|
||||
c.OriginalColumnName != c.ColumnName &&
|
||||
existingByName.Keys.Any(k => string.Equals(k, c.OriginalColumnName, StringComparison.OrdinalIgnoreCase)))
|
||||
.ToList();
|
||||
foreach (var r in renames)
|
||||
{
|
||||
var actualKey = existingByName.Keys.FirstOrDefault(k => string.Equals(k, r.OriginalColumnName, StringComparison.OrdinalIgnoreCase));
|
||||
if (actualKey == null) continue;
|
||||
var renameSql = $"EXEC sp_rename 'dbo.[{request.TableName}].[{actualKey}]', '{r.ColumnName.Replace("'", "''")}', 'COLUMN';";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(renameSql);
|
||||
var oldCol = existingByName[actualKey];
|
||||
existingByName.Remove(actualKey);
|
||||
existingByName[r.ColumnName] = new TableColumnDto { ColumnName = r.ColumnName, DataType = oldCol.DataType, Length = oldCol.Length, Scale = oldCol.Scale, IsNullable = oldCol.IsNullable, IsPrimaryKey = oldCol.IsPrimaryKey, IsIdentity = oldCol.IsIdentity, Comment = oldCol.Comment, DefaultValue = oldCol.DefaultValue ?? "", Order = oldCol.Order };
|
||||
}
|
||||
|
||||
var existingColumnNames = existingByName.Keys.ToHashSet(StringComparer.Ordinal);
|
||||
var columnsToDrop = existingColumnNames.Except(newColumnNames);
|
||||
foreach (var columnName in columnsToDrop)
|
||||
{
|
||||
var dropColumnSql = $"ALTER TABLE [{request.TableName}] DROP COLUMN [{columnName}]";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(dropColumnSql);
|
||||
}
|
||||
|
||||
// 2) 新增列
|
||||
var columnsToAdd = request.Columns.Where(c => !existingColumnNames.Contains(c.ColumnName, StringComparer.Ordinal));
|
||||
foreach (var column in columnsToAdd.OrderBy(c => c.Order))
|
||||
{
|
||||
var definition = $"[{column.ColumnName}] ";
|
||||
var dtLowerAdd = column.DataType.ToLower();
|
||||
|
||||
if (dtLowerAdd == "nvarchar" || dtLowerAdd == "varchar")
|
||||
{
|
||||
var length = column.Length ?? 255;
|
||||
definition += $"{column.DataType}({(length == -1 ? "MAX" : length.ToString())})";
|
||||
}
|
||||
else if (dtLowerAdd == "char" || dtLowerAdd == "nchar")
|
||||
{
|
||||
var length = column.Length ?? 1;
|
||||
definition += $"{column.DataType}({length})";
|
||||
}
|
||||
else if (dtLowerAdd == "decimal" || dtLowerAdd == "numeric")
|
||||
{
|
||||
var precision = column.Length ?? 18;
|
||||
var scale = column.Scale ?? 0;
|
||||
definition += $"{column.DataType}({precision}, {scale})";
|
||||
}
|
||||
else
|
||||
{
|
||||
definition += column.DataType;
|
||||
}
|
||||
|
||||
if (column.IsIdentity)
|
||||
{
|
||||
definition += " IDENTITY(1,1)";
|
||||
}
|
||||
|
||||
if (!column.IsNullable)
|
||||
{
|
||||
definition += " NOT NULL";
|
||||
}
|
||||
|
||||
definition += FormatDefaultForSqlServer(column.DefaultValue);
|
||||
|
||||
var addColumnSql = $"ALTER TABLE [{request.TableName}] ADD {definition}";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(addColumnSql);
|
||||
// 新列注释
|
||||
if (!string.IsNullOrWhiteSpace(column.Comment))
|
||||
{
|
||||
try
|
||||
{
|
||||
var commentSql = $@"
|
||||
EXEC sp_addextendedproperty
|
||||
@name = N'MS_Description',
|
||||
@value = N'{column.Comment.Replace("'", "''")}',
|
||||
@level0type = N'SCHEMA', @level0name = N'dbo',
|
||||
@level1type = N'TABLE', @level1name = N'{request.TableName.Replace("'", "''")}',
|
||||
@level2type = N'COLUMN', @level2name = N'{column.ColumnName.Replace("'", "''")}'";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(commentSql);
|
||||
}
|
||||
catch { /* 注释已存在时忽略 */ }
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Alter existing columns: 类型/长度/小数位/可空(existingByName 已在 rename 后更新)
|
||||
var columnsToAlter = request.Columns
|
||||
.Where(c => existingByName.ContainsKey(c.ColumnName))
|
||||
.OrderBy(c => c.Order);
|
||||
|
||||
foreach (var column in columnsToAlter)
|
||||
{
|
||||
var existing = existingByName[column.ColumnName];
|
||||
|
||||
var defaultChanged = (existing.DefaultValue ?? "").Trim() != (column.DefaultValue ?? "").Trim();
|
||||
|
||||
var newTypeRaw = column.DataType?.Trim() ?? "";
|
||||
var newTypeLower = newTypeRaw.ToLower();
|
||||
var oldTypeLower = (existing.DataType ?? "").Trim().ToLower();
|
||||
|
||||
// 兼容传入 nvarchar(max)/varchar(max)
|
||||
if (newTypeLower == "nvarchar(max)") { newTypeLower = "nvarchar"; column.Length = -1; }
|
||||
if (newTypeLower == "varchar(max)") { newTypeLower = "varchar"; column.Length = -1; }
|
||||
|
||||
bool typeChanged = !string.Equals(oldTypeLower, newTypeLower, StringComparison.OrdinalIgnoreCase);
|
||||
bool nullableChanged = existing.IsNullable != column.IsNullable;
|
||||
|
||||
bool lengthChanged = false;
|
||||
bool scaleChanged = false;
|
||||
if (newTypeLower is "nvarchar" or "varchar")
|
||||
{
|
||||
var oldLen = existing.Length ?? 255;
|
||||
var newLen = column.Length ?? 255;
|
||||
lengthChanged = oldLen != newLen;
|
||||
}
|
||||
else if (newTypeLower is "char" or "nchar")
|
||||
{
|
||||
var oldLen = existing.Length ?? 1;
|
||||
var newLen = column.Length ?? 1;
|
||||
lengthChanged = oldLen != newLen;
|
||||
}
|
||||
else if (newTypeLower is "decimal" or "numeric")
|
||||
{
|
||||
var oldPrec = existing.Length ?? 18;
|
||||
var newPrec = column.Length ?? 18;
|
||||
lengthChanged = oldPrec != newPrec;
|
||||
|
||||
var oldScale = existing.Scale ?? 0;
|
||||
var newScale = column.Scale ?? 0;
|
||||
scaleChanged = oldScale != newScale;
|
||||
}
|
||||
|
||||
bool isIdentityChanged = existing.IsIdentity != column.IsIdentity;
|
||||
|
||||
// 仅当字段有实际修改时才执行更新脚本
|
||||
if (!typeChanged && !nullableChanged && !lengthChanged && !scaleChanged && !defaultChanged && !isIdentityChanged)
|
||||
continue;
|
||||
|
||||
// IsIdentity 变更:SQL Server 不支持 ALTER COLUMN 修改 IDENTITY,需通过临时表重建
|
||||
if (isIdentityChanged)
|
||||
{
|
||||
await RebuildTableForIdentityChangeAsync(request, existingByName);
|
||||
var refreshed = await GetTableInfoAsync(request.TableName);
|
||||
foreach (var c in refreshed.Columns)
|
||||
existingByName[c.ColumnName] = c;
|
||||
break;
|
||||
}
|
||||
|
||||
if (typeChanged || nullableChanged || lengthChanged || scaleChanged)
|
||||
{
|
||||
string typeSql;
|
||||
if (newTypeLower is "nvarchar" or "varchar")
|
||||
{
|
||||
var len = column.Length ?? 255;
|
||||
typeSql = $"{newTypeLower}({(len == -1 ? "MAX" : len.ToString())})";
|
||||
}
|
||||
else if (newTypeLower is "char" or "nchar")
|
||||
{
|
||||
var len = column.Length ?? 1;
|
||||
typeSql = $"{newTypeLower}({len})";
|
||||
}
|
||||
else if (newTypeLower is "decimal" or "numeric")
|
||||
{
|
||||
var precision = column.Length ?? 18;
|
||||
var scale = column.Scale ?? 0;
|
||||
typeSql = $"{newTypeLower}({precision}, {scale})";
|
||||
}
|
||||
else
|
||||
{
|
||||
typeSql = newTypeRaw;
|
||||
}
|
||||
|
||||
var nullSql = column.IsNullable ? "NULL" : "NOT NULL";
|
||||
var alterSql = $"ALTER TABLE [{request.TableName}] ALTER COLUMN [{column.ColumnName}] {typeSql} {nullSql}";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(alterSql);
|
||||
}
|
||||
|
||||
if (defaultChanged)
|
||||
{
|
||||
var dfName = $"DF_{request.TableName}_{column.ColumnName}";
|
||||
var dropDfSql = $@"DECLARE @cn NVARCHAR(200);
|
||||
SELECT @cn = dc.name FROM sys.default_constraints dc
|
||||
INNER JOIN sys.columns c ON dc.parent_object_id = c.object_id AND dc.parent_column_id = c.column_id
|
||||
WHERE c.object_id = OBJECT_ID('{request.TableName.Replace("'", "''")}') AND c.name = N'{column.ColumnName.Replace("'", "''")}';
|
||||
IF @cn IS NOT NULL EXEC('ALTER TABLE [{request.TableName}] DROP CONSTRAINT [' + @cn + ']')";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(dropDfSql);
|
||||
if (!string.IsNullOrWhiteSpace(column.DefaultValue))
|
||||
{
|
||||
var defVal = column.DefaultValue.Trim();
|
||||
var vLower = defVal.ToLower();
|
||||
var innerExpr = vLower is "getdate()" or "newid()" or "sysdatetime()" ? defVal
|
||||
: long.TryParse(defVal, out _) || decimal.TryParse(defVal, out _) ? defVal
|
||||
: vLower == "true" || vLower == "1" ? "1"
|
||||
: vLower == "false" || vLower == "0" ? "0"
|
||||
: $"N'{defVal.Replace("'", "''")}'";
|
||||
var addDfSql = $"ALTER TABLE [{request.TableName}] ADD CONSTRAINT [{dfName}] DEFAULT ({innerExpr}) FOR [{column.ColumnName}]";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(addDfSql);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var existingPkCols = existingTable.Columns.Where(c => c.IsPrimaryKey).OrderBy(c => c.Order).Select(c => c.ColumnName).ToList();
|
||||
var newPkCols = request.Columns.Where(c => c.IsPrimaryKey).OrderBy(c => c.Order).Select(c => c.ColumnName).ToList();
|
||||
var pkChanged = !existingPkCols.SequenceEqual(newPkCols);
|
||||
|
||||
if (pkChanged)
|
||||
{
|
||||
// Drop existing PK constraint
|
||||
var dropPkSql = $@"
|
||||
DECLARE @pkConstraintName NVARCHAR(200);
|
||||
SELECT @pkConstraintName = name
|
||||
FROM sys.key_constraints
|
||||
WHERE type = 'PK' AND parent_object_id = OBJECT_ID('{request.TableName}');
|
||||
IF @pkConstraintName IS NOT NULL
|
||||
EXEC('ALTER TABLE [{request.TableName}] DROP CONSTRAINT [' + @pkConstraintName + ']')";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(dropPkSql);
|
||||
|
||||
if (newPkCols.Any())
|
||||
{
|
||||
var pkConstraintName = $"PK_{request.TableName}";
|
||||
var pkColumns = newPkCols.Select(c => $"[{c}]");
|
||||
|
||||
var addPkSql = $"ALTER TABLE [{request.TableName}] ADD CONSTRAINT [{pkConstraintName}] PRIMARY KEY ({string.Join(", ", pkColumns)})";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(addPkSql);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var column in request.Columns.Where(c => !string.IsNullOrWhiteSpace(c.Comment)))
|
||||
{
|
||||
if (existingByName.TryGetValue(column.ColumnName, out var ec) && (ec.Comment ?? "").Trim() == (column.Comment ?? "").Trim())
|
||||
continue;
|
||||
|
||||
var escapedComment = column.Comment!.Replace("'", "''");
|
||||
var tableNameEscaped = request.TableName.Replace("'", "''");
|
||||
var columnNameEscaped = column.ColumnName.Replace("'", "''");
|
||||
try
|
||||
{
|
||||
var updateCommentSql = $@"
|
||||
EXEC sp_updateextendedproperty
|
||||
@name = N'MS_Description',
|
||||
@value = N'{escapedComment}',
|
||||
@level0type = N'SCHEMA', @level0name = N'dbo',
|
||||
@level1type = N'TABLE', @level1name = N'{tableNameEscaped}',
|
||||
@level2type = N'COLUMN', @level2name = N'{columnNameEscaped}'";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(updateCommentSql);
|
||||
}
|
||||
catch
|
||||
{
|
||||
try
|
||||
{
|
||||
var addCommentSql = $@"
|
||||
EXEC sp_addextendedproperty
|
||||
@name = N'MS_Description',
|
||||
@value = N'{escapedComment}',
|
||||
@level0type = N'SCHEMA', @level0name = N'dbo',
|
||||
@level1type = N'TABLE', @level1name = N'{tableNameEscaped}',
|
||||
@level2type = N'COLUMN', @level2name = N'{columnNameEscaped}'";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(addCommentSql);
|
||||
}
|
||||
catch { /* 忽略 */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteTableAsync(string tableName)
|
||||
{
|
||||
var dropTableSql = $"DROP TABLE [{tableName}]";
|
||||
await _context.SqlSugarClient.Ado.ExecuteCommandAsync(dropTableSql);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user