2

例如,当死锁发生时,后面的 SQL 命令会成功执行,即使它们已经分配了回滚后的 SQL 事务。看来,它是由在 SQL Server 上创建的新隐式事务引起的。

有人可能期望 ADO.NET 会抛出一个异常,即命令正在僵尸事务上执行。但是,不会引发此类异常。(我认为这是 ASP.NET 中的一个错误。)此外,由于僵尸事务,最终会Dispose()默默地忽略回滚。

任何想法,我如何确保没有人可以对隐式事务执行命令?或者,如何检查交易是僵尸?我发现Commit()Rollback()检查了僵尸交易,但是我可以打电话给他们进行测试:)

我还发现阅读 IsolationLevel 也会进行检查,但我不确定transaction.IsolationLevel.ToString();未来的优化器是否不会删除简单的调用。或者您知道任何其他安全方式调用 getter(不使用反射或 IL 发射)吗?

编辑:Remus Rusanu 指出这种情况通常不会发生。是的,这是真的。它通常发生在代码中存在错误时。在我们的例子中,finally 语句中有一些日志记录例程试图将失败存储到数据库中。现在,我正在尝试找到一种解决方案,以便将来检测此类错误。由于这些错误很难测试。如果 ADO.NET 会检查提供的事务是否为僵尸,则更容易发现此错误。我发现了两种可能性:

  1. 关闭隐式事务的创建 - 我不确定这是否可能。
  2. 确保在执行任何命令之前,检查僵尸事务将运行。
4

3 回答 3

2

你描述的不存在。事务回滚将在您的应用程序中引发非常明显的异常。出于某种原因,我更愿意相信您的代码捕获了异常并默默地忽略它,在事务回滚后继续执行语句。

于 2010-06-07T16:07:10.283 回答
0

//根据您的描述,我猜您的代码有效地做到了这一点

        SqlConnection conn = new SqlConnection("ConnectionString");
        SqlCommand cmd = new SqlCommand("insert into ....");

        cmd.Connection = conn;

        conn.Open();

        SqlTransaction tran = conn.BeginTransaction();


        cmd.Transaction = tran;

        tran.Rollback(); //or tran.Dispose();

        cmd.ExecuteNonQuery();

这会导致 cmd 在事务范围之外执行。

删除行 cmd.Connection = conn; 将实现我认为您正在寻找的行为(例如,由于事务不再有效,命令将失败。)

SqlConnection conn = new SqlConnection("ConnectionString");
    SqlCommand cmd = new SqlCommand("insert into ....");

    //cmd.Connection = conn;

    conn.Open();

    SqlTransaction tran = conn.BeginTransaction();
    cmd.Connection = tran.Connection;

    cmd.Transaction = tran;

    tran.Rollback(); //or tran.Dispose();

    cmd.ExecuteNonQuery();
于 2010-06-21T19:18:53.093 回答
0

可能与您的问题没有直接关系,因为它是由错误引起的,但仍然可能引起人们的兴趣。并非所有错误都会导致事务回滚,因此有时事务可能“部分成功”——一些语句出错,而另一些则正常完成。
有一个选项SET XACT_ABORT ON可使服务器在任何错误时中止事务。

考虑到您的问题,您不能关闭隐式事务(如果您执行 SQL 语句,除非另一个事务已经处于活动状态,否则将创建一个隐式事务)。因此,您只需正确处理错误,以确保在您需要时有交易。
查看TransactionScope类,您可以使用它来避免在代码中管理这些事务。

于 2010-06-20T17:34:59.043 回答