使用ADO.NET SqlClient
类时,可以通过几种方式回滚事务。通过显式调用SqlTransaction.Rollback
,或者在事务范围内执行命令超时或事务超时等。
但是,如何在使用SQL Server(2012或更高版本)时检测此回滚何时完成?对于长时间运行的事务,回滚可能需要很长时间,并且它通常在数据库上非常密集,因此立即重试事务可能不明智。在我们的情况下,我们希望等待回滚完成。
(在我们的特定情况下,由于使用TransactionScope类的事务超时而发生回滚,但我更喜欢一种适用于任何类型回滚的方法。)
我看过最初看起来很有希望的sys.dm_exec_requests
。它最初的命令类型为KILL/ROLLBACK
,并报告了进度百分比。 (如果我从原始事务中捕获了SPID,我将能够轮询该表并等待它完成)。但是,大约一半时间我注意到它改为AWAITING COMMAND
(而回滚仍然在执行)。
我也看了KILL <SPID> WITH STATUSONLY
。如果没有正在进行的回滚,这似乎会产生错误,因此它可以用于轮询方法,但我注意到如果我从SSMS运行BEGIN TRAN ... ROLLBACK TRAN
批处理,则会报告错误,表示没有回滚当它回滚时正在进行中。在这种情况下,dm_exec_requests
表报告ROLLBACK TRANSACTION
作为命令类型。
那么如何才能以可靠的方式等待回滚呢?
答案 0 :(得分:0)
您可以根据spid
查看status
的交易活动。列rollback
将告诉您当前的交易状态,并在完成后从SqlChangeMonitor
或消失后更改。
如果您想获得有关.NET的反馈,请使用SqlChangeMonitor(example)来缓存结果并等待rollback
通知cmd.Connection.BeginTransaction(IsolationLevel.ReadCommitted)
的更改
如果您解决了超时问题,可以查看:
使用 IsolationLevel.ReadCommitted 。这样,您的.NET代码将等待提交或回滚的完成。
(cmd
类似于SqlCommand
的{{1}}
答案 1 :(得分:0)
SqlRollback.cs
using System;
using System.Data.SqlClient;
using System.Threading;
namespace Events
{
public class SqlRollback
{
private string _connString = "Server=(localdb)\\mssqllocaldb;Database=QueryIt.EmployeeDb;Trusted_Connection=true";
public event EventHandler TransactionRolledBack;
public void Transact()
{
using (SqlConnection conn = new SqlConnection(_connString))
{
conn.Open();
var command = conn.CreateCommand();
SqlTransaction transaction;
transaction = conn.BeginTransaction("Transaction");
command.Connection = conn;
command.Transaction = transaction;
try
{
transaction.Save("checkpoint");
command.CommandText = "INSERT INTO Employees (name, discriminator) VALUES ('Dimitar', 'Bastun')";
command.ExecuteNonQuery();
throw new Exception();
} catch (Exception ex)
{
transaction.Rollback("checkpoint"); //Rolling back to the checkpoint
Thread.Sleep(5000); //Simulating some time
transaction.Commit();
this.OnTransactionRollingBack(EventArgs.Empty);
}
}
}
private void OnTransactionRollingBack(EventArgs e)
{
EventHandler handler = TransactionRolledBack;
if(handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
}
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Events
{
class Program
{
static void Main(string[] args)
{
var rollBack = new SqlRollback();
rollBack.TransactionRolledBack += RollBack_TransactionRolledBack;
try
{
rollBack.Transact();
} catch (Exception e)
{
}
}
private static void RollBack_TransactionRolledBack(object sender, EventArgs e)
{
Console.WriteLine("Rooled!");
}
}
}