在 ASP.NET Core 中使用 EF Core 进行开启事务工作单元(Unit of Work) 仓储层,服务层模式

在 ASP.NET Core 中使用 Entity Framework Core 实现一个带有事务的工作单元(Unit of Work)模式的仓储层和服务层,可以确保在执行多个数据库操作时具有原子性。这样,即使某个操作出现错误,所有操作也可以回滚。

以下是如何实现这个模式的详细步骤:

  1. 定义实体类
    我们首先定义一个简单的实体类,例如 Product:
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}
  1. 定义数据库上下文
    定义一个继承自 DbContext 的数据上下文类:
public class ApplicationDbContext : DbContext
{
    public DbSet<Product> Products { get; set; }

    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
    {
    }
}
  1. 定义仓储接口和实现
    创建一个通用的仓储接口并实现它:
public interface IRepository<T> where T : class
{
    void Add(T entity);
    void Update(T entity);
    void Delete(T entity);
    Task<T> GetByIdAsync(int id);
    Task<IEnumerable<T>> GetAllAsync();
}

public class Repository<T> : IRepository<T> where T : class
{
    private readonly ApplicationDbContext _context;
    private readonly DbSet<T> _dbSet;

    public Repository(ApplicationDbContext context)
    {
        _context = context;
        _dbSet = context.Set<T>();
    }

    public void Add(T entity) => _dbSet.Add(entity);
    public void Update(T entity) => _dbSet.Update(entity);
    public void Delete(T entity) => _dbSet.Remove(entity);
    public async Task<T> GetByIdAsync(int id) => await _dbSet.FindAsync(id);
    public async Task<IEnumerable<T>> GetAllAsync() => await _dbSet.ToListAsync();
}
  1. 定义工作单元接口和实现,支持事务
    在工作单元实现中,我们将包含事务处理。可以使用 IDbContextTransaction 来控制事务:
public interface IUnitOfWork : IDisposable
{
    IRepository<T> Repository<T>() where T : class;
    Task<int> SaveChangesAsync();
    Task BeginTransactionAsync();
    Task CommitTransactionAsync();
    Task RollbackTransactionAsync();
}

public class UnitOfWork : IUnitOfWork
{
    private readonly ApplicationDbContext _context;
    private IDbContextTransaction _transaction;

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

    public IRepository<T> Repository<T>() where T : class
    {
        return new Repository<T>(_context);
    }

    public async Task BeginTransactionAsync()
    {
        _transaction = await _context.Database.BeginTransactionAsync();
    }

    public async Task CommitTransactionAsync()
    {
        await _transaction.CommitAsync();
    }

    public async Task RollbackTransactionAsync()
    {
        await _transaction.RollbackAsync();
    }

    public async Task<int> SaveChangesAsync()
    {
        return await _context.SaveChangesAsync();
    }

    public void Dispose()
    {
        _context.Dispose();
    }
}
  1. 创建服务层
    服务层使用工作单元和仓储,确保在需要时可以开启和提交事务:
public interface IProductService
{
    Task<Product> CreateProductAsync(Product product);
    Task<bool> DeleteProductAsync(int id);
}

public class ProductService : IProductService
{
    private readonly IUnitOfWork _unitOfWork;

    public ProductService(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }

    public async Task<Product> CreateProductAsync(Product product)
    {
        await _unitOfWork.BeginTransactionAsync();
        try
        {
            _unitOfWork.Repository<Product>().Add(product);
            await _unitOfWork.SaveChangesAsync();
            await _unitOfWork.CommitTransactionAsync();
        }
        catch
        {
            await _unitOfWork.RollbackTransactionAsync();
            throw; // 可以抛出异常,交由上层处理
        }
        return product;
    }

    public async Task<bool> DeleteProductAsync(int id)
    {
        await _unitOfWork.BeginTransactionAsync();
        try
        {
            var product = await _unitOfWork.Repository<Product>().GetByIdAsync(id);
            if (product == null) return false;

            _unitOfWork.Repository<Product>().Delete(product);
            await _unitOfWork.SaveChangesAsync();
            await _unitOfWork.CommitTransactionAsync();
        }
        catch
        {
            await _unitOfWork.RollbackTransactionAsync();
            throw; // 处理异常
        }
        return true;
    }
}
  1. 在 Startup 类中注册服务
    在 Startup.cs 文件中,进行依赖注入的配置:
public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddScoped<IUnitOfWork, UnitOfWork>();
    services.AddScoped<IProductService, ProductService>();
}
  1. 使用服务层
    最后,创建一个控制器来使用服务层:
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly IProductService _productService;

    public ProductsController(IProductService productService)
    {
        _productService = productService;
    }

    [HttpPost]
    public async Task<IActionResult> Create(Product product)
    {
        var createdProduct = await _productService.CreateProductAsync(product);
        return CreatedAtAction(nameof(GetById), new { id = createdProduct.Id }, createdProduct);
    }

    [HttpDelete("{id}")]
    public async Task<IActionResult> Delete(int id)
    {
        var result = await _productService.DeleteProductAsync(id);
        if (!result) return NotFound();
        return NoContent();
    }
}

重要注意事项
事务的使用:每次添加或删除产品时都会开启事务(BeginTransactionAsync),并在操作成功时提交事务(CommitTransactionAsync),在操作失败时回滚事务(RollbackTransactionAsync)。

异常处理:在服务层中,建议捕获异常并进行适当处理,可以记录日志或抛出自定义异常。

分离关注点:将数据访问逻辑、业务逻辑和控制逻辑分开,有助于提升代码的可维护性和可测试性。

通过这个模式,你可以确保在执行多个数据库操作时的原子性,保持数据的一致性和完整性。

posted @ 2024-10-29 17:28  一个人走在路上  阅读(155)  评论(0编辑  收藏  举报