zoukankan      html  css  js  c++  java
  • 回滚事务

    我们没有使用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 
      

  • 相关阅读:
    Windows快捷键
    visual studio code颜色主题切换
    visual studio code中文语言包安装
    顶点缓存与索引缓存
    程序结构(2)
    ansible常用模块
    ansible常用模块
    ubuntu实用技巧
    ubuntu实用技巧
    Sqoop导出MySQL数据
  • 原文地址:https://www.cnblogs.com/wichell/p/2397120.html
Copyright © 2011-2022 走看看