当通过C#存在外部依赖项时,如何截断表
本文关键字:何截断 依赖 存在 外部 | 更新日期: 2023-09-27 18:21:04
出于测试目的,我有一个方法,它接受一个SqlConnection
或一个连接字符串和一个字符串。该方法根据一组"危险"字符串检查SqlConnection
或DbContext.Database.Connection.ConnectionString
,如果连接字符串有任何"危险"的字符串,则不执行其查询。
我基本上需要知道如何执行(通过C#)截断/删除表中的所有数据,但问题是外部依赖关系。我正在尝试以下方法。删除所有依赖项,删除表,然后恢复所有依赖项。但是,我的恢复所有依赖关系代码有问题。我如何通过C#实现这一点?
应删除表的主方法。
public int DeleteFromDatabase(SqlConnection sqlConnection, string tableName)
{
int success = 0;
string sqlTrunc = "Delete from " + tableName;
if (isSafeSqlConnection(sqlConnection))
{
DropAllConstraints();
using (sqlConnection)
{
SqlCommand cmd = new SqlCommand(sqlTrunc, sqlConnection);
sqlConnection.Open();
success = cmd.ExecuteNonQuery();
sqlConnection.Close();
}
ReinstateAllConstraints(); //<=error happens here.
}
return success;
}
这将删除所有限制:
public void DropAllConstraints()
{
string[] queries = File.ReadAllLines(@"Utility'UnsafeStrings'dropallcontraint.txt");
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
foreach (var item in queries)
{
var command = new SqlCommand(item, connection);
command.ExecuteNonQuery();
}
connection.Close();
}
}
这会检查传入的连接是否不是活动服务器:
private bool isSafeSqlConnection(SqlConnection connection)
{
string pathToUNsafeStrings = @"Utility'UnsafeStrings'UnsafeStrings.txt";
string[] unsafeStrings = File.ReadAllLines(pathToUNsafeStrings);
foreach (var item in unsafeStrings)
{
if (connection.ConnectionString.Contains(item))
return false;
}
return true;
}
这个方法基本上执行从这个查询返回的每个条目:
select
'ALTER TABLE dbo.' + object_name(fk.parent_object_id) +
' ADD CONSTRAINT ' + fk.name +
' FOREIGN KEY(' + c1.name + ') REFERENCES dbo.' +
object_name(fk.referenced_object_id) + '(' + c2.name + ')' as col1
from
sys.foreign_keys fk
inner join
sys.foreign_key_columns fkc ON fk.object_id = fkc.constraint_object_id
inner join
sys.columns c1 ON fkc.parent_column_id = c1.column_id and c1.object_id = fkc.parent_object_id
inner join
sys.columns c2 ON fkc.referenced_column_id = c2.column_id and c2.object_id = fkc.referenced_object_id
public void ReinstateAllConstraints()
{
string[] reinstateAllConstraints = File.ReadAllLines(@"Utility'UnsafeStrings'reisntateconstraint.txt");
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
foreach (var item in reinstateAllConstraints)
{
var command = new SqlCommand(item, connection);
command.ExecuteNonQuery();
}
connection.Close();
}
}
您可以执行以下工作流:
- 禁用外键检查
- 从引用要删除的表的表中删除所有寄存器
- 从表中删除所有要删除的寄存器
- 启用外键检查
您可以在下面的代码中看到(我使用的是C#6)
public bool TruncateTable(string tableName)
{
string sqlTrunc = $"Delete from {tableName}";
if (isSafeSqlConnection(sqlConnection))
{
DisableAllForeignKeys();
using (sqlConnection)
{
DeleteAllDependencies(tableName, sqlConnection);
SqlCommand cmd = new SqlCommand(sqlTrunc, sqlConnection);
sqlConnection.Open();
success = cmd.ExecuteNonQuery();
sqlConnection.Close();
}
EnableAllForeignKeys();
}
return success;
}
public void DisableAllForeignKeys(SqlConnection sqlConnection)
{
using(var command = new SqlCommand($"EXEC sp_msforeachtable '"ALTER TABLE ? NOCHECK CONSTRAINT all'"", sqlConnection))
command.ExecuteNonQuery();
}
public void EnableAllForeignKeys(SqlConnection sqlConnection)
{
using(var command = new SqlCommand($"EXEC sp_msforeachtable '"ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all'"", sqlConnection))
command.ExecuteNonQuery();
}
private static void DeleteAllDependencies(string tableName, SqlConnection sqlConnection)
{
var sql =
$@"SELECT t.name AS 'Table that contains FK',
fk.name AS 'FK Name',
t1.Name AS 'Table that is being referenced'
FROM sys.foreign_key_columns fkc
INNER JOIN sys.tables t ON t.object_id = fkc.parent_object_id
INNER JOIN sys.tables t1 ON t1.object_id = fkc.referenced_object_id
INNER JOIN sys.columns c1 ON c1.object_id = fkc.parent_object_id AND c1.column_id = fkc.parent_column_id
INNER JOIN sys.foreign_keys fk ON fk.object_id = fkc.constraint_object_id
INNER JOIN sys.schemas sc ON t.schema_id = sc.schema_id
WHERE (sc.name + '.' +t1.name) = 'dbo.{
tableName}';";
var command = sqlConnection.CreateCommand();
command.CommandText = sql;
List<Tuple<string, string, string>> tuples;
using (var dataReader = command.ExecuteReader())
{
var enumerator = dataReader.GetEnumerator();
tuples = Enumerable.Range(1, int.MaxValue)
.TakeWhile(i => enumerator.MoveNext())
.Select(i => (IDataRecord)enumerator.Current)
.Select(dr => Tuple.Create(dr.GetString(0), dr.GetString(1), dr.GetString(2)))
.ToList();
}
foreach (var tuple in tuples)
{
using (var sqlCommand = sqlConnection.CreateCommand())
{
sqlCommand.CommandText = $"DELETE FROM {tuple.Item1}";
sqlCommand.ExecuteNonQuery();
}
}
}
您可能需要配置关系以相应地传播更改("级联"),这样当关系的一部分被删除时,另一部分也会被删除或更新。
DBMS正确地指出,如果您从另一个表中引用的项丢失,则不能激活fk关系。