【发布时间】: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