回滚事务

我们没有使用TDD,所以单元测试最麻烦的就是准备测试的基础数据。我们现在是使用内存仓储来做单元测试,要为每个仓储都构造基础数据,非常麻烦。

前几天看xunit的源码,看到AutoRollbackAttribute这个特性,异常的兴奋 ^_^。怎么就忘了用事务的自动回滚呢?

我们看AutorollbackAttribute的具体实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class AutoRollbackAttribute : BeforeAfterTestAttribute
{
     IsolationLevel isolationLevel = IsolationLevel.Unspecified;
     TransactionScope scope;
     TransactionScopeOption scopeOption = TransactionScopeOption.Required;
     long timeoutInMS = -1;
 
     /// <summary>
     /// Gets or sets the isolation level of the transaction.
     /// Default value is <see cref="IsolationLevel"/>.Unspecified.
     /// </summary>
     public IsolationLevel IsolationLevel
     {
         get { return isolationLevel; }
         set { isolationLevel = value; }
     }
 
     /// <summary>
     /// Gets or sets the scope option for the transaction.
     /// Default value is <see cref="TransactionScopeOption"/>.Required.
     /// </summary>
     public TransactionScopeOption ScopeOption
     {
         get { return scopeOption; }
         set { scopeOption = value; }
     }
 
     /// <summary>
     /// Gets or sets the timeout of the transaction, in milliseconds.
     /// By default, the transaction will not timeout.
     /// </summary>
     public long TimeoutInMS
     {
         get { return timeoutInMS; }
         set { timeoutInMS = value; }
     }
 
     /// <summary>
     /// Rolls back the transaction.
     /// </summary>
     public override void After(MethodInfo methodUnderTest)
     {
         scope.Dispose();
     }
 
     /// <summary>
     /// Creates the transaction.
     /// </summary>
     public override void Before(MethodInfo methodUnderTest)
     {
         TransactionOptions options = new TransactionOptions();
         options.IsolationLevel = isolationLevel;
         if (timeoutInMS > 0)
             options.Timeout = new TimeSpan(timeoutInMS * 10);
         scope = new TransactionScope(scopeOption, options);
     }
}

这里使用了.Net Framework自带的TransactionScope。TransactionScope在.NET 2.0中就已经有了,可用于分布式事务。用这种方法来做数据的自动回滚也有一些不足:

  1. 数据库要支持事务。
  2. 内部数据库操作的逻辑里没有事务的实现。

很庆幸的是我们的项目正好都满足上面的2点,唯一不足的就是mongodb不支持事务。所以就需要混合仓储实现了,事务数据库使用真实的仓储,mongodb使用内存仓储。

项目中是用VS自带的单元测试框架,也不想因为这一个特性而改用xunit,那就只能动手把这个迁移到VS的单元测试框架里了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/// <summary>
/// 单元测试基类
/// </summary>
[TestClass]
public class BaseUnitTest
{
     IsolationLevel _isolationLevel = IsolationLevel.Unspecified;
     TransactionScopeOption _scopeOption = TransactionScopeOption.Required;
     TransactionScope _transactionScope;
     bool _openAutoRollback = true ;
 
     /// <summary>
     /// 构造函数
     /// </summary>
     /// <param name="autoRollback">是否开启自动回滚,默认开启</param>
     public BaseUnitTest( bool autoRollback = true )
     {
         _openAutoRollback = autoRollback;
     }
 
     /// <summary>
     /// 自动回滚事务初始化
     /// </summary>
     [TestInitialize]
     public void AutoRollbackBefore()
     {
         if (_openAutoRollback)
         {
             var options = new TransactionOptions();
             options.IsolationLevel = _isolationLevel;
             options.Timeout = new TimeSpan(0, 1, 0);
             _transactionScope = new TransactionScope(_scopeOption, options);
         }
     }
 
     /// <summary>
     /// 自动回滚事务回滚并释放对象
     /// </summary>
     [TestCleanup]
     public void AutoRollbackAfter()
     {
         if (_openAutoRollback)
         {
             if (_transactionScope == null )
                 throw new InvalidOperationException( "未初始化TransactionScope" );
             //回滚事务
             _transactionScope.Dispose();
             //释放事务对象
             _transactionScope = null ;
             //移除所有的缓存
             RemoveHttpRuntimeCache();
         }
     }
 
     /// <summary>
     /// 移除所有的HttpRuntime缓存
     /// </summary>
     [DebuggerStepThrough]
     private void RemoveHttpRuntimeCache()
     {
         var cache = HttpRuntime.Cache.GetEnumerator();
         var keys = new List< string >();
         while (cache.MoveNext())
         {
             keys.Add(cache.Key.ToString());
         }
         foreach (var key in keys)
         {
             HttpRuntime.Cache.Remove(key);
         }
     }
 
     /// <summary>
     /// 设置不自动回滚事务
     /// </summary>
     protected void SetAutoRollbackIsUnavailabled()
     {
         _openAutoRollback = false ;
     }
}

上面的RemoveHttpRuntimeCache是因为我们在项目中有使用HttpRuntime缓存,关系数据库中的数据回滚后会导致缓存和数据库不一致,所以一但有开启事务的自动回滚,也要相应的清空内存缓存。

方法很简单,跟大家分享一下,和TransactionScope相关的知识,不清楚的同学可以看下MSDN关于“TransactionScope”的文档

原文地址:http://blog.moozi.net/archives/use-the-transaction-rollback-to-unit-test.html 
  

你可能感兴趣的:(事务)