假设我有一个如下所示的数据库方法:
public void insertRow(SqlConnection c)
{
using (var cmd = new SqlCommand("insert into myTable values(@dt)",c))
{
cmd.Parameters.Add(new SqlParameter("@dt",DbType.DateTime)).Value = DateTime.Now;
cmd.ExecuteNonQuery();
}
}
现在假设我想测试这个方法。因此,我编写了一个测试用例,尝试将此方法包装在事务中,以便在测试插入结果后可以回滚更改:
public void testInsertRow()
{
SqlConnection c = new SqlConnection("connection.string.here");
SqlTransaction trans = c.BeginTransaction();
insertRow();
// do something here to evaluate what happened, e.g. query the DB
trans.Rollback();
}
然而,这无效,因为:
当分配给命令的连接处于挂起的本地事务中时,ExecuteNonQuery要求命令具有事务。该命令的Transaction属性尚未初始化。
有没有办法实现这一点,无需重写每个数据库方法来接受事务,然后重写每个调用以将null
传递给事务的方法?
例如,这可行:
public void insertRow(SqlConnection c, SqlTransaction t)
{
using (var cmd = new SqlCommand("insert into myTable values(@dt)",c))
{
if (t != null) cmd.Transaction = t;
cmd.Parameters.Add(new SqlParameter("@dt",DbType.DateTime)).Value = DateTime.Now;
cmd.ExecuteNonQuery();
}
c.Close();
}
但是,我必须重写每次调用数据库方法以包含null
参数,或者为每个自动传递null
的数据库方法写入覆盖签名,例如
public void insertRow(SqlConnection c) { insertRow(c, null); }
允许基于事务的数据库调用测试的最佳方法是什么?
答案 0 :(得分:2)
您可以使用TransactionScope自动将连接添加到交易中
public void testInsertRow()
{
using(TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
SqlConnection c = new SqlConnection("connection.string.here");
insertRow(c);
// do something here to evaluate what happened, e.g. query the DB
//do not call scope.Complete() so we get a rollback.
}
}
现在,如果您运行多个并行测试,这将导致测试相互阻塞。如果您将数据库设置为支持它,则可以执行快照隔离,因此并发测试的更新不会互相锁定。
public void testInsertRow()
{
using(TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew,
new TransactionOptions(IsolationLevel = IsolationLevel.Snapshot))
{
SqlConnection c = new SqlConnection("connection.string.here");
insertRow(c);
// do something here to evaluate what happened, e.g. query the DB
//do not call scope.Complete() so we get a rollback.
}
}