NopCommerce现在最新版是3.9,不过依然没有事务机制。作为一个商城,我觉得事务也还是很有必要的。以下事务代码以3.9版本作为参考:
首先,IDbContext接口继承IDisposable接口,以便手动释放相关资源,并添加一个新方法CurrentEntries,目的是得到跟踪实体的当前跟踪状态(主要作用是使用事务回滚后改变当前实体对应的状态):
/// <summary> /// 得到跟踪实体的当前跟踪状态 /// </summary> /// <returns></returns> IEnumerable<DbEntityEntry> CurrentEntries();
自然相应的IDbContext接口实现类NopObjectContext也要实现该方法:
public IEnumerable<DbEntityEntry> 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); } }
如果你觉得对你有帮助,右侧打个赏呗!
Authori:黄仲秋
QQ:875755898