C#中的工作单元和存储库模式

作者:微信公众号:【架构师老卢】
8-29 13:43
7

概述:在现代 .NET 开发中,确保关注点分离、干净的代码和高效的数据处理至关重要。Unit of Work 和 Repository 模式被广泛用于简化数据访问,而依赖注入 (DI) 已成为提高性能和可扩展性的标准实践。在本文中,我将展示如何有效地实现这些模式。存储库模式Repository Pattern 抽象了数据访问层,提供了一种与数据库交互的方法,而不会暴露底层数据库操作的复杂性。基础存储库我们将定义一个基本接口,该接口可以处理任何实体的基本 CRUD 操作:IRepositoryTpublic interface IRepositoryT where T : class{ Task

在现代 .NET 开发中,确保关注点分离、干净的代码和高效的数据处理至关重要。Unit of WorkRepository 模式被广泛用于简化数据访问,而依赖注入 (DI) 已成为提高性能和可扩展性的标准实践。在本文中,我将展示如何有效地实现这些模式。

存储库模式

Repository Pattern 抽象了数据访问层,提供了一种与数据库交互的方法,而不会暴露底层数据库操作的复杂性。

基础存储库

我们将定义一个基本接口,该接口可以处理任何实体的基本 CRUD 操作:IRepository<T>

public interface IRepository<T> where T : class
{
    Task<List<T>> GetAllAsync();
    Task<T> GetByIdAsync(int id);
    Task AddAsync(T entity);
    void Update(T entity);
    void Delete(T entity);
}

接下来,我们实现 class:Repository<T>

public class Repository<T> : IRepository<T> where T : class
{
    protected readonly DbContext _context;
    protected readonly DbSet<T> _dbSet;

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

    public async Task<List<T>> GetAllAsync()
    {
        return await _dbSet.ToListAsync();
    }

    public async Task<T> GetByIdAsync(int id)
    {
        return await _dbSet.FindAsync(id);
    }

    public async Task AddAsync(T entity)
    {
        await _dbSet.AddAsync(entity);
    }

    public void Update(T entity)
    {
        _dbSet.Update(entity);
    }

    public void Delete(T entity)
    {
        _dbSet.Remove(entity);
    }
}

在这里,异步方法确保数据库操作不会阻塞主线程,从而提高性能和可扩展性。

特定存储库

对于更复杂的实体,例如 ,我们可能需要基本 CRUD 操作之外的其他方法。例如,检索用户及其关联的订单:User

// User Repository
public interface IUserRepository : IRepository<User>
{
    Task<User> GetUserWithOrdersAsync(int id);
}

public class UserRepository : Repository<User>, IUserRepository
{
    public UserRepository(DbContext context) : base(context) { }

    public async Task<User> GetUserWithOrdersAsync(int id)
    {
        return await _context.Set<User>()
            .Include(u => u.Orders)
            .FirstOrDefaultAsync(u => u.Id == id);
    }
}

// Order Repository
public interface IOrderRepository : IRepository<Order>
{
    Task<List<Order>> GetOrdersByUserIdAsync(int userId);
}

public class OrderRepository : Repository<Order>, IOrderRepository
{
    public OrderRepository(DbContext context) : base(context) { }

    public async Task<List<Order>> GetOrdersByUserIdAsync(int userId)
    {
        return await _dbSet.Where(o => o.UserId == userId).ToListAsync();
    }
}

工作单元模式

Unit of Work 模式确保特定业务事务中的所有操作都作为一个操作提交或回滚。我们使用依赖注入 (DI) 将存储库注入到类中,从而使我们能够集中事务管理。UnitOfWork

public class UnitOfWork : IUnitOfWork, IDisposable
{
    private readonly DbContext _context;
    private readonly IServiceProvider _serviceProvider;
    private bool _disposed = false;

    public UnitOfWork(DbContext context, IServiceProvider serviceProvider)
    {
        _context = context;
        _serviceProvider = serviceProvider;
    }

    public IUserRepository UserRepository => _serviceProvider.GetService<IUserRepository>();
    public IOrderRepository OrderRepository => _serviceProvider.GetService<IOrderRepository>();

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

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                _context.Dispose();
            }
        }
        _disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

在此 中,我们用于异步保存所有更改。存储库通过 Lazilyly 解析,以确保它们仅在必要时被实例化。UnitOfWorkCompleteAsync()IServiceProvider

使用 DTO 传输实体数据

在现代应用程序中,DTO (数据传输对象) 通常用于在客户端和服务器之间传输数据。控制器不应直接与实体打交道,而应依赖 DTO 来确保数据模型和 API 协定之间的明确分离。

public class UserDto  
{  
    public string Name { get; set; }  
    public string Email { get; set; }  
}  
  
public class OrderDto  
{  
    public string ProductName { get; set; }  
    public decimal Price { get; set; }  
}  
  
public class CreateUserWithOrderDto  
{  
    public UserDto User { get; set; }  
    public OrderDto Order { get; set; }  
}

DTO 抽象了基础实体的详细信息,从而允许在客户端和服务器之间实现更安全、更清晰的通信。

服务层:处理业务逻辑

服务层包含应用程序的业务逻辑,保证控制器只协调数据流。以下是我们构建 :UserService

public class UserService
{
    private readonly IUnitOfWork _unitOfWork;

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

    public async Task<UserDto> GetUserByIdAsync(int id)
    {
        var user = await _unitOfWork.UserRepository.GetUserWithOrdersAsync(id);
        if (user == null) return null;

        return new UserDto { Name = user.Name, Email = user.Email };
    }

    public async Task CreateUserAndOrderAsync(UserDto userDto, OrderDto orderDto)
    {
        // Create a User
        var user = new User { Name = userDto.Name, Email = userDto.Email };
        await _unitOfWork.UserRepository.AddAsync(user);

        // Create an Order tied to the user
        var order = new Order { ProductName = orderDto.ProductName, Price = orderDto.Price, UserId = user.Id };
        await _unitOfWork.OrderRepository.AddAsync(order);

        // Save both User and Order in a single transaction
        await _unitOfWork.CompleteAsync();
    }

   public async Task DeleteUserAndOrdersAsync(int userId)
    {
        // Find the user
        var user = await _unitOfWork.UserRepository.GetByIdAsync(userId);
        if (user != null)
        {
            // Find and delete user's orders
            var orders = await _unitOfWork.OrderRepository.GetOrdersByUserIdAsync(userId);
            foreach (var order in orders)
            {
                _unitOfWork.OrderRepository.Delete(order);
            }

            // Delete the user after orders are deleted
            _unitOfWork.UserRepository.Delete(user);

            // Save both User and Order deletions in a single transaction
            await _unitOfWork.CompleteAsync();
        }
    }
}
  • CreateUserAndOrderAsync:该方法在单个事务中创建用户和关联的订单。如果任何操作失败,则回滚整个事务。
  • DeleteUserAndOrdersAsync:在单个事务中删除用户及其所有关联的订单。

在 .NET 中配置依赖关系注入

要完成设置,我们需要在 DI 容器中注册 repositories、 和 service 类:UnitOfWork

public void ConfigureServices(IServiceCollection services)  
{  
    services.AddDbContext<ApplicationDbContext>(options =>  
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));  
  
    // Register repositories and UnitOfWork  
    services.AddScoped<IUserRepository, UserRepository>();  
    services.AddScoped<IOrderRepository, OrderRepository>();  
    services.AddScoped<IUnitOfWork, UnitOfWork>();  
  
    // Register services  
    services.AddScoped<UserService>();  
}

通过此配置,我们确保 and 存储库被注入到需要的地方。UnitOfWork

控制器层:

控制器专注于处理 HTTP 请求和响应,将所有业务逻辑委托给服务层。以下是设置 :UsersController

[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
    private readonly UserService _userService;

    public UsersController(UserService userService)
    {
        _userService = userService;
    }

    [HttpGet("{id}")]
    public async Task<IActionResult> GetUserAsync(int id)
    {
        var user = await _userService.GetUserByIdAsync(id);
        if (user == null) return NotFound();

        return Ok(user);
    }

    [HttpPost]
    public async Task<IActionResult> CreateUserWithOrderAsync([FromBody] CreateUserWithOrderDto createUserWithOrderDto)
    {
        await _userService.CreateUserAndOrderAsync(createUserWithOrderDto.User, createUserWithOrderDto.Order);
        return CreatedAtAction(nameof(GetUserAsync), new { id = createUserWithOrderDto.User.Name }, createUserWithOrderDto.User);
    }

    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteUserAndOrdersAsync(int id)
    {
        await _userService.DeleteUserAndOrdersAsync(id);
        return NoContent();
    }
}

主要改进:

  1. 事务一致性:创建用户及其订单或删除用户及其关联订单等操作使用工作单元模式在同一事务中处理。
  2. 关注点分离:控制器只负责协调请求和响应,而服务层处理所有业务逻辑。

Unit of WorkRepository 模式是用于在 C# 应用程序中维护事务一致性关注点分离的强大工具。当与 Dependency Injection 结合使用时,您将获得一个干净、可扩展的架构,该架构既可维护又高性能。

阅读排行