NopCommerce添加事务机制

NopCommerce,一直没有事务机制。作为一个商城,我觉得事务也还是很有必要的。以下事务代码以3.9版本作为参考:

首先,IDbContext接口继承IDisposable接口,以便手动释放相关资源,并添加一个新方法CurrentEntries,目的是得到跟踪实体的当前跟踪状态(主要作用是使用事务回滚后改变当前实体对应的状态):

        /// <summary>
        /// 得到跟踪实体的当前跟踪状态
        /// </summary>
        /// <returns></returns>
        IEnumerable<DbEntityEntry> CurrentEntries();

自然相应的IDbContext接口实现类NopObjectContext也要实现该方 CurrentEntries() { return ChangeTracker.Entries(); }

注意:主项目代码添加这个方法之后,所有需要操作数据库的插件都要实现该方法,这个大家自行斟酌,如果插件也需要事务的话。

添加一个接口命名IUnitOfWork,如下:

 

复制代码
    public interface IUnitOfWork : IDisposable
    {
        /// <summary>
        /// 开启事务
        /// </summary>
        /// <param name="isolationLevel"></param>
        void BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified);

        /// <summary>
        /// 提交
        /// </summary>
        void Commit();

        /// <summary>
        /// 回滚
        /// </summary>
        void Rollback();

        /// <summary>
        /// 释放资源
        /// </summary>
        /// <param name="disposing">是否释放</param>
        void Dispose(bool disposing);
    }
复制代码

 

并实现该接口,添加实现类命名UnitOfWork,如下:

复制代码
    public class UnitOfWork : IUnitOfWork
    {
        private IDbContext _context;
        private ObjectContext _objectContext;
        private IDbTransaction _transaction;

        private bool _disposed;

        public UnitOfWork(IDbContext context)
        {
            _context = context;
        }

        public void BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified)
        {
            _objectContext = ((IObjectContextAdapter)_context).ObjectContext;
            if (_objectContext.Connection.State != ConnectionState.Open)
                _objectContext.Connection.Open();

            _transaction = _objectContext.Connection.BeginTransaction(isolationLevel);
        }

        public void Commit()
        {
            _transaction.Commit();
        }

        public void Rollback()
        {
            _transaction.Rollback();
            foreach (var entry in _context.CurrentEntries())
            {
                switch (entry.State)
                {
                    case EntityState.Modified:
                        entry.State = EntityState.Unchanged;
                        break;
                    case EntityState.Added:
                        entry.State = EntityState.Detached;
                        break;
                    case EntityState.Deleted:
                        entry.State = EntityState.Unchanged;
                        break;
                }
            }
        }

        public void Dispose(bool disposing)
        {
            if (_disposed)
                return;

            if (disposing)
            {
                try
                {
                    if (_objectContext != null && _objectContext.Connection.State == ConnectionState.Open)
                        _objectContext.Connection.Close();
                }
                catch (ObjectDisposedException)
                {
                }
                if (_context != null)
                {
                    _context.Dispose();
                    _context = null;
                }
            }

            _disposed = true;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }
复制代码

代码很好理解,我就不多做注释了,不清楚的自行网上了解。

下面附上我写的单元测试:

复制代码
    [TestClass]
    public class UnitTest1
    {
        protected static NopObjectContext Context = new NopObjectContext(ConfigurationManager.ConnectionStrings["ConnectionStr"].ToString());
        private readonly IUnitOfWork _unitOfWork = new UnitOfWork(Context);
        protected readonly IRepository<ForumGroup> ForumGroupRepository = new EfRepository<ForumGroup>(Context);
        protected readonly IRepository<Setting> SettingRepository = new EfRepository<Setting>(Context);

        [TestMethod]
        public void Can_Commit_Test()
        {
            try
            {
                _unitOfWork.BeginTransaction(); // 开启事务

                var forumGroup = new ForumGroup
                {
                    Name = "ForumGroup1", // 自行建立Name的唯一约束测试,测试两次第二次会自行回滚
                    DisplayOrder = 1,
                    CreatedOnUtc = DateTime.Now,
                    UpdatedOnUtc = DateTime.Now.AddDays(1)
                };
                ForumGroupRepository.Insert(forumGroup); // 第一次插入数据

                var setting = new Setting
                {
                    Name = "test_transaction_name",
                    Value = "test_transaction_value",
                    StoreId = 1
                };
                SettingRepository.Insert(setting);

                _unitOfWork.Commit(); // 提交
            }
            catch (Exception)
            {
                _unitOfWork.Rollback(); // 回滚
            }

            Assert.AreEqual(ForumGroupRepository.TableNoTracking.Count(), 1);
Assert.AreEqual(SettingRepository.TableNoTracking.Count(x => x.Name == "test_transaction_name"), 1); } }
复制代码
posted on 2017-12-05 18:04  zrSoldier  阅读(699)  评论(2编辑  收藏