【问题标题】:Transaction not rolling back in c#事务在 c# 中不回滚
【发布时间】:2012-11-04 00:30:06
【问题描述】:
private void btnConfigure_Click(object sender, EventArgs e)
{
    try
    {
        dbConfigure dc = new dbConfigure();
        SqlTransaction tr = conn.BeginTransaction();
        cmd.Transaction = tr;
        if (dc.configuration(cmd, ps.tableNames))
            tr.Commit();
        else
        {
            tr.Rollback();
            mesg.show("Transaction is Rolled back");
        }
    }
    catch (Exception ex)
    {
        mesg.show(ex.Message);
    }
}

如果我在 configuration 方法的任何地方遇到问题,那么它会返回 false,我可以看到消息 Transaction is Rolled Back。但实际上事务并没有完全回滚,并且尽管回滚,但该函数对数据库结构所做的一些更改仍然存在,这是非常不希望的。我的问题是事务回滚发生故障的可能性是什么?

我的项目中除了共享(以上)方法之外没有其他任何事务

小细节

我正在调用我班级 dbConfigure 的一个非常冗长/复杂的函数 configuration。它对数据库结构进行了一些必要的更改。例如它

  1. 丢弃外键
  2. 删除主键
  3. 删除自动增量字段

    它会在删除之前保存这些键并以所需的顺序/位置重新创建

conn 是一个已经打开的SqlConnection,除此之外我没有使用任何连接

cmd 是 conn.CreateCommand() 除此以外我不使用任何命令

在整个过程中我从不关闭连接,但是 SqlDataReader 在完成工作时会在 configuration 函数中关闭。

【问题讨论】:

  • 非常感谢@Morten Jacobsen。我不这么认为。所以我得到了我的答案。但是,如果有任何方法可以做到这一点,请您也指导一下吗?意思是如果另一个创建表失败后回滚创建表?
  • 是的@GeorgeStocker。尽管您告诉的链接似乎对解决我的问题没有多大帮助。但是我很高兴我通过这个问题学到了一些东西。最好的答案可能是帮助我找到一种方法来回滚数据库中的结构更改。但至少您的评论提供了一些指导和一些这样做的动力。您可以将其发布为答案,如果我找不到更好的答案,我肯定会接受它。因为它是有效的答案
  • 我已经删除了我之前的评论,因为正如 Remus Rusanu 所指出的,实际上可以在 SQL Server 中执行事务 DDL。我的评论基于不可能的 Oracle 数据库。

标签: c# sql-server transactions rollback


【解决方案1】:

对数据库结构的更改不是事务性的,因此您不能回滚创建新表,例如

BS。大多数 DDL 是事务性的,可以回滚。只有涉及与非事务性组件(如文件系统,例如将新文件添加到数据库)交互的更改不能回滚。如果在活动事务中调用任何非事务性 DDL,也会非常明确地引发异常。

添加和修改表是非常明确的事务性,可以很容易地用一个例子来说明:

begin transaction;
create table foo (a int);
select * from sys.tables where object_id = object_id('foo');
rollback;
select * from sys.tables where object_id = object_id('foo');

所以问题出在OP缺少代码,部分没有贴出来。

作为一般评论,应尽可能使用 System.Transactions(考虑到the default constructor is broken)。如果使用 SqlConnection.BeginTransaction 最好还是依赖 IDisposable:

using (SqlTransaction trn = conn.BeginTransaction())
{
   ...
   trn.Commit ();
}

System.Transactions 应该受到青睐,因为它们不依赖于代码规则,事务范围内的任何代码 SqlClient 都会自动注册。

顺便说一句,配置函数在错误时引发,而不是返回错误。

关于潜在的真正问题:如何处理无法在单个事务中注册的冗长、复杂的迁移(例如,它可能会生成太多日志)。答案是唯一可行的选择是在迁移开始时进行数据库备份,如果迁移失败,则从该备份中恢复。为每个迁移操作提供手动、测试和可靠的补偿操作以撤消迁移的替代方法非常困难、容易出错,并且最终没有必要,因为从备份中恢复非常简单和可证明正确的。

【讨论】:

  • 你是对的,这在 SQL 服务器中是可能的(我不知道)。但是,你也许应该指出它的供应商特定 - 据我所知,Oracle 不允许它。由于 OP 没有指定他连接到哪个数据库,这种方法可能不可行。
  • @MortenJacobsen:问题被标记为“sql-server”,并且 OP 代码使用 SqlClient 组件。至于 DDL 交易能力,答案要复杂得多:stackoverflow.com/a/4736346/105929 但最终没有必要讨论,因为这个问题非常明确地针对特定供应商。
  • 我看到了,没注意到标签。
  • 好答案。我会花一些时间:)
【解决方案2】:

这是否可以通过使用 TransactionScope 而不是在您的代码中创建 SqlTransaction 来完成。像这样的:

using (TransactionScope transaction = new TransactionScope())
{
    ...perform your database action here

    scope.Complete()
}

【讨论】:

  • 可能是,但我认为不是。据我所知,结构的变化无法回滚。可能有办法
猜你喜欢
  • 1970-01-01
  • 2017-08-19
  • 2011-09-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-13
相关资源
最近更新 更多