ASP.NET MVC5清洁架构中如何在EF中使用事务?
清洁架构下ASP.NET MVC5事务原子操作实现方案
我已阅读Ardalis的清洁架构文档及eShopOnWeb示例,在ASP.NET MVC5项目中实现了该架构,并完成了IUnitOfWork的实现。项目分为三层:
- ApplicationCore
- Infrastructure
- UI
ApplicationCore层定义
仓储接口
public interface IRepository<TEntity> where TEntity : class { List<TEntity> GetAll(ISpecification<TEntity> specification = null); TEntity GetById(int id); void Add(TEntity entity); void AddRange(IEnumerable<TEntity> entities); void Update(TEntity entity); void Delete(TEntity entity); }
IUnitOfWork接口
public interface IUnitOfWork { void BeginTransaction(); IRepository<TEntity> GetRepository<TEntity>() where TEntity : class; void Commit(); void TransactionCommit(); void Rollback(); }
Infrastructure层实现
EfRepository实现
public class EfRepository<TEntity> : IRepository<TEntity> where TEntity : class { private readonly OrderExchangeEntities _context; private readonly DbSet<TEntity> _entities; public EfRepository(OrderExchangeEntities context) { _context = context; _entities = _context.Set<TEntity>(); } public List<TEntity> GetAll(ISpecification<TEntity> specification = null) { if (specification != null) { return _entities.Where(specification.Criteria).ToList(); } return _entities.ToList(); } public TEntity GetById(int id) { return _entities.Find(id); } public void Add(TEntity entity) { _entities.Add(entity); } public void Update(TEntity entity) { _entities.Attach(entity); _context.Entry(entity).State = EntityState.Modified; } public void Delete(TEntity entity) { _entities.Remove(entity); } public void AddRange(IEnumerable<TEntity> entities) { _context.Set<TEntity>().AddRange(entities); } }
EFUnitOfWork实现
public class EFUnitOfWork : IUnitOfWork { private readonly OrderExchangeEntities _dbContext; private IDbTransaction _transaction; public EFUnitOfWork(OrderExchangeEntities dbContext) { _dbContext = dbContext; } public void BeginTransaction() { _transaction = _dbContext.Database.Connection.BeginTransaction(); } public void Commit() { _dbContext.SaveChanges(); } public IRepository<TEntity> GetRepository<TEntity>() where TEntity : class { return new EfRepository<TEntity>(_dbContext); } public void Rollback() { _transaction?.Rollback(); } public void TransactionCommit() { try { _transaction?.Commit(); } catch { _transaction?.Rollback(); throw; } } }
现有代码问题分析
当前OrderPaymentMessageService的事务实现存在三个核心问题:
- 错误调用了不存在的
_unitOfWork.SaveChanges()方法,导致部分操作提前持久化,无法回滚 - 事务提交流程混乱,未结合
Commit()(上下文变更写入)与TransactionCommit()(数据库事务提交) - 未确保
_userDocumentsService和_orderServices共享同一UnitOfWork上下文,若它们使用独立DbContext,事务无法覆盖其操作
正确原子操作实现
修改后的OrderPaymentMessageService
public class OrderPaymentMessageService : IOrderPaymentMessageService { private readonly IUnitOfWork _unitOfWork; private readonly IAppLogger _applogger; private readonly IOrderService _orderServices; private readonly IUserDocumentsService _userDocumentsService; private IRepository<OrderPaymentMessage> _orderPaymentMessageRepository; // 移除直接注入的仓储,统一通过UnitOfWork获取,保证上下文一致性 public OrderPaymentMessageService(IUnitOfWork unitOfWork, IAppLogger applogger, IOrderService orderServices, IUserDocumentsService userDocumentsService) { _unitOfWork = unitOfWork; _orderPaymentMessageRepository = _unitOfWork.GetRepository<OrderPaymentMessage>(); _applogger = applogger; _orderServices = orderServices; _userDocumentsService = userDocumentsService; } public bool Create(OrderPaymentMessage orderPaymentMessage) { _unitOfWork.BeginTransaction(); try { Guard.Against.Null(orderPaymentMessage, nameof(orderPaymentMessage)); // 1. 执行支付消息添加操作 _orderPaymentMessageRepository.Add(orderPaymentMessage); // 2. 执行文档删除操作,失败则回滚 var documentsList = _userDocumentsService.GetAllDocumentsByOrderID(orderPaymentMessage.OrderID.GetValueOrDefault()) .Select(x => x.Id).ToList(); bool isDeleteSuccess = _userDocumentsService.DeleteRange(documentsList); if (!isDeleteSuccess) { _unitOfWork.Rollback(); return false; } // 3. 执行订单更新操作,失败则回滚 bool isOrderUpdated = _orderServices.MarkAdminSendPaymentMessage(1, 1, DateTime.Now); if (!isOrderUpdated) { _unitOfWork.Rollback(); return false; } // 4. 先将所有变更写入数据库(事务未提交) _unitOfWork.Commit(); // 5. 提交事务,完成原子操作 _unitOfWork.TransactionCommit(); return true; } catch (Exception ex) { _unitOfWork.Rollback(); _applogger.LogError(ex); return false; } } public List<OrderPaymentMessageModel> GetMessageByOrderID(int orderID) { try { Guard.Against.Zero(orderID, nameof(orderID)); var query = new GetOrderPaymentMessageByOrderID(orderID); return _orderPaymentMessageRepository.GetAll(query).Select(x => new OrderPaymentMessageModel { OrderID = x.OrderID, ID = x.ID, Message = x.Message, CreatedDate = x.CreatedDate }).ToList(); } catch (Exception ex) { _applogger.LogError(ex); return null; } } }
关键注意事项
- 上下文一致性:
IOrderService和IUserDocumentsService内部必须通过同一IUnitOfWork实例获取仓储,否则它们的操作不在当前事务范围内。需修改这些服务的构造函数,注入IUnitOfWork而非直接注入仓储。 - 事务流程规范:严格遵循「开启事务 → 执行所有业务操作 → 保存上下文变更 → 提交事务」的顺序,任何步骤失败立即回滚。
- 异常处理优化:
TransactionCommit中捕获异常回滚后需重新抛出,确保上层感知错误。
内容的提问来源于stack exchange,提问作者Ahsan Ali Siddiqi
相关产品推荐
相关产品推荐

