.NET Core 仓储模式(Repository Pattern)完整教程

仓储模式是领域驱动设计(DDD) 和 .NET 项目中最常用的设计模式之一,核心作用是隔离业务逻辑与数据访问层,让代码更易维护、测试和扩展。


一、什么是仓储模式?

核心概念

  • 仓储(Repository) :充当业务逻辑数据库之间的中介,类似内存中的集合。
  • 作用 :业务层只调用仓储方法,不需要关心数据是怎么存/取的(EF Core、Dapper、内存都可以)。
  • 优势
    1. 解耦:数据访问逻辑和业务逻辑分离
    2. 可测试:无需真实数据库即可单元测试
    3. 统一数据访问规范,代码更整洁
    4. 易于切换数据访问技术(EF Core ↔ Dapper)

标准结构

arduino 复制代码
项目
├─ Models/Entities       // 实体类(对应数据库表)
├─ Data                  // 数据库上下文
├─ Repositories
│   ├─ IRepository.cs    // 泛型仓储接口(基础CRUD)
│   ├─ Repository.cs     // 泛型仓储实现
│   ├─ IUserRepo.cs      // 特定实体仓储接口
│   └─ UserRepo.cs       // 特定实体仓储实现
└─ Services/Controllers  // 调用仓储

二、快速实现(.NET Core + EF Core)

1. 准备工作

安装 NuGet 包:

bash 复制代码
Install-Package Microsoft.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.SqlServer

2. 定义实体(示例:User)

csharp 复制代码
// Entities/User.cs
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}

3. 创建数据库上下文

csharp 复制代码
// Data/AppDbContext.cs
using Microsoft.EntityFrameworkCore;

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

    public DbSet<User> Users => Set<User>();
}

4. 定义泛型仓储接口(所有实体通用)

csharp 复制代码
// Repositories/IRepository.cs
using System.Linq.Expressions;

public interface IRepository<T> where T : class
{
    // 查询
    Task<T> GetByIdAsync(int id);
    Task<List<T>> GetAllAsync();
    Task<List<T>> FindAsync(Expression<Func<T, bool>> predicate);

    // 新增/修改/删除
    Task AddAsync(T entity);
    Task UpdateAsync(T entity);
    Task DeleteAsync(T entity);

    // 保存
    Task SaveChangesAsync();
}

5. 实现泛型仓储基类

csharp 复制代码
// Repositories/Repository.cs
using Microsoft.EntityFrameworkCore;
using System.Linq.Expressions;

public class Repository<T> : IRepository<T> where T : class
{
    protected readonly AppDbContext _dbContext;
    private readonly DbSet<T> _dbSet;

    // 依赖注入 DbContext
    public Repository(AppDbContext dbContext)
    {
        _dbContext = dbContext;
        _dbSet = _dbContext.Set<T>();
    }

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

    public async Task<List<T>> GetAllAsync() => await _dbSet.ToListAsync();

    public async Task<List<T>> FindAsync(Expression<Func<T, bool>> predicate)
    {
        return await _dbSet.Where(predicate).ToListAsync();
    }

    public async Task AddAsync(T entity) => await _dbSet.AddAsync(entity);

    public Task UpdateAsync(T entity)
    {
        _dbSet.Update(entity);
        return Task.CompletedTask;
    }

    public Task DeleteAsync(T entity)
    {
        _dbSet.Remove(entity);
        return Task.CompletedTask;
    }

    public async Task SaveChangesAsync() => await _dbContext.SaveChangesAsync();
}

6. 特定实体仓储(可选,扩展通用方法)

csharp 复制代码
// IUserRepository.cs
public interface IUserRepository : IRepository<User>
{
    // 扩展:根据姓名查询用户
    Task<User> GetUserByNameAsync(string name);
}

// UserRepository.cs
public class UserRepository : Repository<User>, IUserRepository
{
    public UserRepository(AppDbContext dbContext) : base(dbContext) { }

    public async Task<User> GetUserByNameAsync(string name)
    {
        return await _dbContext.Users.FirstOrDefaultAsync(u => u.Name == name);
    }
}

7. 注册依赖注入(Program.cs)

csharp 复制代码
// 注册数据库上下文
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

// 注册泛型仓储 + 特定仓储
builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
builder.Services.AddScoped<IUserRepository, UserRepository>();

8. 在 Controller 中使用仓储

csharp 复制代码
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
    private readonly IUserRepository _userRepository;

    // 构造函数注入
    public UserController(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    [HttpGet("{id}")]
    public async Task<IActionResult> Get(int id)
    {
        var user = await _userRepository.GetByIdAsync(id);
        return Ok(user);
    }

    [HttpPost]
    public async Task<IActionResult> Add(User user)
    {
        await _userRepository.AddAsync(user);
        await _userRepository.SaveChangesAsync();
        return Created("", user);
    }
}

三、进阶:工作单元模式(Unit of Work)

工作单元 + 仓储 是企业级开发的最佳组合 ,解决多仓储事务统一提交问题。

1. IUnitOfWork 接口

csharp 复制代码
public interface IUnitOfWork : IDisposable
{
    IUserRepository Users { get; }
    Task<int> SaveChangesAsync();
}

2. UnitOfWork 实现

csharp 复制代码
public class UnitOfWork : IUnitOfWork
{
    private readonly AppDbContext _dbContext;
    public IUserRepository Users { get; }

    public UnitOfWork(AppDbContext dbContext)
    {
        _dbContext = dbContext;
        Users = new UserRepository(_dbContext);
    }

    public async Task<int> SaveChangesAsync() => await _dbContext.SaveChangesAsync();

    public void Dispose() => _dbContext.Dispose();
}

3. 注册 + 使用

csharp 复制代码
// Program.cs
builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();

// Controller
private readonly IUnitOfWork _uow;
public UserController(IUnitOfWork uow) => _uow = uow;

// 调用
await _uow.Users.AddAsync(user);
await _uow.SaveChangesAsync();  // 统一提交

四、仓储模式最佳实践

  1. 不要滥用:简单单表操作,直接用泛型仓储即可;复杂业务再建专用仓储。
  2. IQueryable 慎用 :不要在接口返回 IQueryable,会破坏隔离性,推荐返回 List/Task<T>
  3. 搭配工作单元:多表操作必须用 UoW 保证事务一致性。
  4. 依赖注入:始终使用 DI 注入仓储,不要手动 new。
  5. 异步优先 :.NET Core 全程使用 async/await 提升性能。

五、适用场景

  • 中大型企业应用
  • 需要单元测试的项目
  • 数据访问层需要灵活切换(SQL/MySQL/内存)
  • 多人协作、规范统一的项目

总结

  1. 仓储模式 = 数据访问层封装,解耦业务与数据库
  2. 泛型仓储提供通用 CRUD,专用仓储扩展个性化方法
  3. 工作单元解决多仓储事务提交问题
  4. .NET Core 中通过依赖注入使用,代码简洁易维护
相关推荐
叫我少年7 小时前
Quartz.NET 调度框架:从入门到封装实战
后端
Java编程爱好者7 小时前
MySQL事务实战:MySQL实例 · 隔离级别 · InnoDB实现机制
后端
砍材农夫7 小时前
物联网 基于netty构建mqtt服务udp支持
后端
JavaAgent架构师7 小时前
Spring AI接入OpenAI报错401/429?3种原因+完整解决代码
人工智能·后端
.NET修仙日记7 小时前
.NET EFCore批量插入性能优化实战:30秒 → 0.5秒
性能优化·c#·.net·.netcore·微软技术·efcore·踩坑实录
子兮曰7 小时前
AI Coding 为什么全选了 TUI?从 Claude Code 到 Codex CLI,终端架构的底层逻辑
前端·后端·ai编程
voyaqi7 小时前
从零设计企业级校验框架:Spring Boot + SPI 实战指南
spring boot·后端·log4j
XovH7 小时前
Django 从 0 到 1 打造完整电商平台:电商项目需求分析与数据库设计
后端
可乐泡枸杞7 小时前
《我用AI,一个月做出学了吗APP》
前端·人工智能·后端