4

如果 sql 调用失败,比如由于死锁而超时,事务可能会变成僵尸事务——我猜我的代码或框架代码会回滚。SqlTransaction 不为空,但如果您尝试执行 Rollback(),它是一个僵尸可能会引发错误。我找不到 .IsZombie 属性。

// Make sure the transaction is not null
if (transaction != null)
{
    //TODO: Is there a way to test a transaction to see if it can be rolled back?
    transaction.Rollback();  
}
4

4 回答 4

2

您可以尝试使用 .NET 2.0 的 System.Transactions 命名空间中的 TransactionScope 类。此类允许您指定超时时间,在该超时时间之后事务将自动取消并回滚。.NET 2.0+ 中的 ADO.NET 支持 TransactionScope,如果在调用数据库时存在 DbTransaction,它将自动在范围内注册一个 DbTransaction:

public void DoSomething()
{
    using (TransactionScope scope = new TransactionScope(TransactionScopeOptions.Required, TimeSpan.FromSeconds(60)))
    {
        MyDac();

        scope.Complete(); // If timeout occurrs, this line is never hit, scope is disposed, which causes rollback if Complete() was not called
    }
}

public class MyDac()
{

    using (SqlConnection ...)
    {
        using (SqlCommand ...)
        {
            // Do something with ADO.NET here...it will autoenroll if a transaction scope is present
        }
    }
}

TransactionScope 在内部创建一个 System.Transactions.Transaction,如果只涉及单个服务器,则默认情况下允许对 SQL Server 进行轻量级事务。如果事务涉及多个服务器或分布式资源管理器,TransactionScope封装的Transaction会被提升为分布式事务,这需要MSDTC协调,这会使TransactionScope的使用变得复杂。如果您的所有事务都是轻量级的,那么 TransactionScope 可以比手动管理您的数据库事务提供很多好处。

于 2009-05-23T17:17:06.080 回答
1

我请你原谅,但我无法避免不同意。客户事务使业务流程原子操作成为可能。如果您想将所有事务操作移入 DB,则总是将业务逻辑移入其中。这是一种方法,但如果您将在程序中使用一些稍微复杂的逻辑,则强烈不推荐。有 while/for/foreachs、字符串检查和其他琐碎的操作真的很繁重,要移入 DB(有时甚至是不可能的)。然而,死锁提示似乎非常有用,并为客户端应用程序提供了进一步的控制(在我看来,这是最好的方法)。

干杯

于 2009-12-21T11:08:05.660 回答
1

我知道的老问题,但我最近正在处理这个问题,并为自己创建了一个小助手函数来安全地提交/回滚 SqlTransactions。我想我会发布它,以防其他人正在寻找这个问题的解决方案。

public void CompleteTransaction(SqlTransaction transaction, bool isRollback) {
    if (transaction == null) { return; }
    try {
        if (isRollback) { 
            transaction.Rollback(); 
        } else { 
            transaction.Commit(); 
        }
    } catch (InvalidOperationException ex) {
        // In my case, I just ignored exceptions due to zombie transactions, 
        // but you could handle it differently depending on your needs
        if (ex.TargetSite == null || ex.TargetSite.ToString() != "Void ZombieCheck()") {
            throw; // Not a zombie transaction, so re-throw the exception
        }
    }
}

当我检查因僵尸事务引发的异常抛出的异常详细信息时,我注意到 TargetSite 是Void ZombieCheck(),因此我的解决方案假定如果 TargetSite 未设置为该值,则它不是由于僵尸事务而导致的错误。

于 2018-05-01T17:00:53.783 回答
0

您可能需要考虑将事务处理代码放在数据库中,然后您可以测试@@ERROR = 1205 以确定您的查询是否是死锁的受害者,在这种情况下您可以重试或回滚。允许客户端应用程序创建和管理事务有点冒险,如果可能的话最好避免。

希望这可以帮助,

账单

于 2009-05-23T12:57:04.600 回答