【问题标题】:How to kill ambient transaction?如何杀死环境事务?
【发布时间】:2021-12-02 20:35:14
【问题描述】:

我有一个与数据库相关的程序。我需要对代表存储库的类中的一些方法进行单元测试。我决定不使用localdb,而是使用Rollback 属性实现:

public class Rollback : Attribute, ITestAction
{
    private TransactionScope transaction;

    public void BeforeTest(ITest test)
    {
        transaction = new TransactionScope();
    }

    public void AfterTest(ITest test)
    {
        transaction.Dispose();
    }

    public ActionTargets Targets => ActionTargets.Test;
}

我是从网上得到的。该属性在方法代码之前隐式开始事务,并在之后回滚。真的很好用。

但有一次我想调试这个测试:

    [Test, Rollback]
    public async Task AddingExistingDictionaryTypeThrowsExceptionTest()
    {
        await _repository.AddDictionaryType(tempDictionaryTypeName, tempDictionaryTypeDescription);

        Assert.ThrowsAsync<Exception>(async () => { await _repository.AddDictionaryType(tempDictionaryTypeName, tempDictionaryTypeDescription); });
    }

AddDictionaryType - 是来自存储库的方法。如果这样的记录不存在,它会在检查后将新实体添加到数据库:

    public async Task AddDictionaryType(string name, string description)
    {
        try
        {
            var sameNameCollection = _dbContext.DictionaryTypes.FromSqlRaw(@$"select * from dictionary_type where name = '{name}'");
            var sameDescriptionCollection = _dbContext.DictionaryTypes.FromSqlRaw(@$"select * from dictionary_type where description = '{description}'");

            if (sameNameCollection.Any() || sameDescriptionCollection.Any())
            {
                throw new AddingExistingDictionaryException();
            }

            _dbContext.Add(new DictionaryType(name, description));
            await _dbContext.SaveChangesAsync();
        }
        catch (Exception ex)
        {
            throw ex;
        }            
    }

不要关注两个SELECTS,我知道,我可能会做一个检查,但那是我的需要。

所以,我在第一次调用AddDictionaryType 方法后设置了点以检查数据库中的新记录,之后我在SSMS 中完成了SELECT(我知道,这也很愚蠢,因为方法在事务中工作)来自同一个表,我试图在其中插入记录。所以,我有一个错误。现在我将告诉你最有趣的:

之后我无法正常执行测试,总是报错:“此连接已与环境事务一起使用。需要完成原始环境事务才能在外部使用此连接”。

所以我认为,存在尚未结束的交易。

问题是我找不到它并杀死它。

我已经通过这样的查询来找到它:

SELECT * FROM sys.sysprocesses;

SELECT * from sys.dm_tran_current_transaction with(nolock);

从 sys.dm_tran_active_transactions 中选择 *;

执行 SP_who2;

从系统中选择 *。 dm_exec_sessions;

SELECT * FROM fn_dblog(NULL, NULL);

我见过进程,试图杀死它们,但没有帮助。 我重新加载了服务器,没有帮助。 交易日志中没有关于此类交易的任何信息。

不,我明白,这样的测试是个大问题,因为我什至不知道如何命名该事务以通过我的手回滚它。

可能不是因为交易?但我没有任何想法。你呢?

【问题讨论】:

  • 如果您在交易对象上使用using 块,它们将自动回滚。不要滚动你自己的异常处理。例如,catch {throw ex; 就是完全错误的。
  • @Charlieface,我没听错吧?你说,使用块回滚任何数据库更改?我不这么认为))可能是我误解了......至于抛出:我抛出我自己的异常,我需要它们。
  • 确实这么认为。一个using 块调用TransactionScope.Dispose 其中the docs say“使用C# using 构造确保即使发生异常也调用此方法。调用此方法后发生的异常可能不会影响事务。此方法还将环境事务恢复到原始状态。” catch (Exception ex) { throw ex; } 只是重新抛出了原来的异常,但是擦除了堆栈跟踪,没有用。
  • @Charlieface,哦,我明白了,谢谢。但是您希望我在 using 语句中添加什么内容?存储库对象?还是 TransactionScope 对象?
  • TransactionScope 对象。任何持有它的东西也必须在using

标签: c# sql visual-studio transactions ssms


【解决方案1】:

我拒绝使用Rollback 属性,但请关注 查理菲斯的建议。现在我在每个测试中使用using 语句,一切正常:

    [Test]
    public async Task Test()
    {
        using (var tran = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
        {
            // test logic
        }
    }

另外,我知道,数据库中有任何卡住的事务。

【讨论】:

    猜你喜欢
    • 2019-05-19
    • 2015-05-29
    • 2019-08-24
    • 1970-01-01
    • 2020-04-09
    • 2011-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多