You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 06:54:25