ASP.NET Core Web API中工作单元与通用仓储模式落地疑问及排查
问题排查与落地指导
现有代码存在的核心问题
1. API层问题
- 手动实例化
DatabaseContext c = new DatabaseContext();:完全多余,违背依赖注入原则,直接删除即可。 Add方法逻辑错误:category_service.TAdd(category)返回的是Task(无返回值异步方法),判断result != null永远为真,且未正确等待异步操作完成。
2. 业务层问题
- 直接依赖仓储而非工作单元:无法实现跨多个仓储的事务管理,失去工作单元模式的核心价值。
- 构造函数参数拼写错误:
_cateogoryDal应为_categoryDal。
3. 通用仓储问题
- 增删改方法自行调用
SaveChangesAsync():每个操作单独提交事务,导致工作单元无法统一控制事务。 Update方法使用同步SaveChanges():与其他异步方法不一致,存在线程阻塞风险。
4. 工作单元(UOW)问题
- 事务管理逻辑错误:
SaveChanges中使用using (transaction)会自动释放事务,导致后续无法复用事务。 - 同步方法混用:
SaveChanges、BeginNewTransaction等未使用EF的异步API,不符合异步编程规范。 - 仓储实例重复创建:
GetRepository<T>每次都新建仓储对象,可优化为缓存实例。
落地改造步骤
步骤1:重构通用仓储,移除内部事务提交
修改GenericRepository,仅做实体状态变更,将事务提交交给工作单元统一处理:
public class GenericRepository<T> : IGenericDal<T> where T : class { protected DatabaseContext dbContext; public GenericRepository(DatabaseContext context) { dbContext = context; } public async Task Delete(T t) { dbContext.Remove(t); // 移除 await dbContext.SaveChangesAsync(); } public IQueryable<T> FindByCondition(Expression<Func<T, bool>> expression) { return dbContext.Set<T>() .Where(expression) .AsNoTracking(); } public async Task<T> GetByID(int id) { return await dbContext.Set<T>().FindAsync(id); } public async Task<List<T>> GetListAll() { return await dbContext.Set<T>().ToListAsync(); } public async Task<List<T>> GetListAll(Expression<Func<T, bool>> filter) { return await dbContext.Set<T>() .Where(filter) .ToListAsync(); } public async Task Insert(T t) { await dbContext.AddAsync(t); // 移除 await dbContext.SaveChangesAsync(); } public async Task Update(T t) { var updatedEntity = dbContext.Entry(t); updatedEntity.State = EntityState.Modified; await Task.CompletedTask; // 保持方法异步签名 // 移除 dbContext.SaveChanges(); } }
步骤2:修正工作单元的事务与异步逻辑
更新IUnitOfWork接口为异步:
public interface IUnitOfWork : IDisposable { Task<bool> BeginNewTransactionAsync(); Task<bool> RollBackTransactionAsync(); IGenericDal<T> GetRepository<T>() where T : class; Task<int> SaveChangesAsync(); }
重构UnitOfWorkRepository:
public class UnitOfWorkRepository : IUnitOfWork { private readonly DatabaseContext _dbContext; private IDbContextTransaction? _transaction; private bool _disposed; private readonly Dictionary<Type, object> _repositories = new(); public UnitOfWorkRepository(DatabaseContext dbContext) { _dbContext = dbContext; } public async Task<bool> BeginNewTransactionAsync() { try { _transaction = await _dbContext.Database.BeginTransactionAsync(); return true; } catch { return false; } } protected virtual void Dispose(bool disposing) { if (!_disposed) { if (disposing) { _transaction?.Dispose(); _dbContext.Dispose(); } } _disposed = true; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } public IGenericDal<T> GetRepository<T>() where T : class { if (_repositories.TryGetValue(typeof(T), out var repo)) { return (IGenericDal<T>)repo; } var newRepo = new GenericRepository<T>(_dbContext); _repositories.Add(typeof(T), newRepo); return newRepo; } public async Task<bool> RollBackTransactionAsync() { try { if (_transaction != null) { await _transaction.RollbackAsync(); _transaction.Dispose(); _transaction = null; } return true; } catch { return false; } } public async Task<int> SaveChangesAsync() { if (_dbContext == null) { throw new ArgumentException("Context is null"); } try { int result; if (_transaction != null) { result = await _dbContext.SaveChangesAsync(); await _transaction.CommitAsync(); _transaction.Dispose(); _transaction = null; } else { result = await _dbContext.SaveChangesAsync(); } return result; } catch (Exception ex) { if (_transaction != null) { await _transaction.RollbackAsync(); _transaction.Dispose(); _transaction = null; } throw new Exception("Error on SaveChanges", ex); } } }
步骤3:业务层改为依赖工作单元
修改CategoryManager,通过工作单元获取仓储并统一提交事务:
public class CategoryManager:ICategoryService { private readonly IUnitOfWork _unitOfWork; private readonly ICategoryDal _categoryDal; public CategoryManager(IUnitOfWork unitOfWork) { _unitOfWork = unitOfWork; _categoryDal = _unitOfWork.GetRepository<Category>() as ICategoryDal; } public async Task<List<Category>> TGetAll() { return await _categoryDal.GetListAll(); } public async Task<Category> TGetById(int id) { return await _categoryDal.GetByID(id); } public async Task TAdd(Category entity) { await _categoryDal.Insert(entity); await _unitOfWork.SaveChangesAsync(); } public async Task TDelete(Category entity) { await _categoryDal.Delete(entity); await _unitOfWork.SaveChangesAsync(); } public async Task TUpdate(Category entity) { await _categoryDal.Update(entity); await _unitOfWork.SaveChangesAsync(); } // 示例:跨仓储事务操作 public async Task<bool> BatchAdd(Category category, Product product) { await _unitOfWork.BeginNewTransactionAsync(); try { await _categoryDal.Insert(category); var productRepo = _unitOfWork.GetRepository<Product>(); await productRepo.Insert(product); await _unitOfWork.SaveChangesAsync(); return true; } catch { await _unitOfWork.RollBackTransactionAsync(); return false; } } }
步骤4:修正API层逻辑
[Route("api/[controller]")] [ApiController] public class CategoriesController : ControllerBase { private readonly ICategoryService _categoryService; public CategoriesController(ICategoryService categoryService) { _categoryService = categoryService; } [HttpGet("getall")] public async Task<IActionResult> Get() { return Ok(await _categoryService.TGetAll()); } [HttpGet("getbyid/{id}")] public async Task<IActionResult> GetByIdCategory(int id) { var category = await _categoryService.TGetById(id); return category != null ? Ok(category) : NotFound(); } [HttpPost("add")] public async Task<IActionResult> Add(Category category) { if (!ModelState.IsValid) { return BadRequest(ModelState); } try { await _categoryService.TAdd(category); return Ok(category); } catch (Exception ex) { return BadRequest(ex.Message); } } }
步骤5:注册依赖注入
在Program.cs中添加服务注册:
builder.Services.AddDbContext<DatabaseContext>(options => { options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")); }); builder.Services.AddScoped<IUnitOfWork, UnitOfWorkRepository>(); builder.Services.AddScoped<ICategoryDal, CategoryRepository>(); builder.Services.AddScoped<ICategoryService, CategoryManager>();
内容的提问来源于stack exchange,提问作者NewPartizal
相关产品推荐
相关产品推荐

