提问人:Steve S 提问时间:9/12/2023 最后编辑:CharliefaceSteve S 更新时间:9/13/2023 访问量:61
如何确保在没有显式 ROLLBACK 的情况下回滚
How to ensure rollback from without explicit ROLLBACK
问:
如果由于 Catch 块而导致 ROLLBACK 超出范围,假设这是原因,除了将所有保存放在一个方法中之外,我应该如何解决?
SqlConnection connection = new SqlConnection(DataAccess.ConnectionString);
connection.Open();
string sql = "USE IncidentReports; BEGIN TRANSACTION;";
SqlCommand cmd = new SqlCommand(sql, connection);
cmd.ExecuteNonQuery();
cmd.Dispose();
try
{
//if anyone of these saves fails, I want to rollback everything. No partial saves
SaveIncident(connection);
if (SelectedStatus.Key != _previousStatus) { SaveStatusChange(connection); }
SaveEmployeeData(connection);
SaveNotes(connection);
SaveChecklist(connection);
SaveExpenses(connection);
_previousStatus = SelectedStatus.Key;
HasBeenSaved = true;
NewIncident = false;
sql = "USE IncidentReports; COMMIT TRANSACTION;";
cmd = new SqlCommand(sql, connection);
cmd.ExecuteNonQuery();
cmd.Dispose();
}
catch (Exception ex)
{
sql = "USE IncidentReports; ROLLBACK TRANSACTION;";
cmd = new SqlCommand(sql, connection);
cmd.ExecuteNonQuery();
cmd.Dispose();
var mbp2 = new MessageBoxParams()
{
Caption = StaticGlobals.MSGBOX_CAPTION,
Image = MessageBoxImage.Exclamation,
Button = MessageBoxButton.OK,
Message = $"Incident was not saved, the following error occurred;{Environment.NewLine}{ex.Message}"
};
ShowMessageBox(mbp2);
return;
}
finally
{
cmd?.Dispose();
connection?.Close();
}
答:
3赞
Charlieface
9/12/2023
#1
您不应该手动执行 和 的事务,除非它们都在同一个批次中(以及 )。BEGIN
COMMIT
SET XACT_ABORT ON
如果你想要一个客户端事务(即你希望事务在任何事务之前开始,并继续直到所有命令完成),你应该只在对象上使用。SqlCommand
BeginTransaction
SqlConnection
然后,使用该对象创建每个命令。最后你打电话。SqlTransaction
transaction.Commit();
此外:
- 在连接、命令和事务对象上放置一个。那么你就不需要 .
using
finally
- 而不是 ,将要连接的数据库放入连接字符串中。
USE
- 在关闭连接之前,不要使用消息框阻止调用线程。
- 请考虑使用 以保持 UI 响应。
async
await
try
{
using var connection = new SqlConnection(DataAccess.ConnectionString);
connection.Open();
using var tran = connection.BeginTransaction();
SaveIncident(connection, tran);
if (SelectedStatus.Key != _previousStatus)
{
SaveStatusChange(connection, tran);
}
SaveEmployeeData(connection, tran);
SaveNotes(connection, tran);
SaveChecklist(connection, tran);
SaveExpenses(connection, tran);
_previousStatus = SelectedStatus.Key;
HasBeenSaved = true;
NewIncident = false;
tran.Commit();
}
catch (Exception ex)
{
var mbp2 = new MessageBoxParams()
{
Caption = StaticGlobals.MSGBOX_CAPTION,
Image = MessageBoxImage.Exclamation,
Button = MessageBoxButton.OK,
Message = $"Incident was not saved, the following error occurred;{Environment.NewLine}{ex.Message}"
};
ShowMessageBox(mbp2);
return;
}
评论
0赞
Steve S
9/14/2023
“ShowMessageBox()”是 UI 线程正在侦听的事件。我已经采纳了你的其他建议。“USE <database>”只是在SSMS中工作的残余,我连接到多个数据库。它位于连接字符串中,因此是冗余的。谢谢。
2赞
MoseLey
9/12/2023
#2
您可以创建数据库事务对象
var transaction = connection.BeginTransaction();
并将事务引用到每个 SqlCommand 的 Transaction 属性
command.Transaction = transaction;
手术后,您可以使用
transaction.Commit();
or
transaction.Rollback();
或者,可以在 SQL 查询中使用条件回滚
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION;
我不确定,但您可以尝试为您的交易提供别名,例如
BEGIN TRANSACTION A
COMMIT TRANSACTION A
ROLLBACK TRANSACTION A
如果没有帮助,您可以检查一下。
评论