个人博客

记录和分享IT工作、学习日常

[AI] .NET Core EF Repository模式详解


0 | Cable Fan | 01/01/0001 00:00:00 | 11/28/2025 11:53:24 | 01/01/0001 00:00:00

.NET Core EF Repository模式详解

在.NET Core中使用Entity Framework (EF) Core时,Repository模式是一种常见的数据访问抽象模式。它用于将数据访问逻辑与业务逻辑分离,使得代码更加可维护和可测试。

Repository的一般用法是创建一个泛型接口,定义常见的CRUD操作,然后为每个实体类型实现这个接口。此外,我们还可以为特定的实体创建自定义的Repository接口,以包含一些特定的查询。

下面是一个常见的Repository模式实现示例:

  1. 定义泛型Repository接口,包含常见的CRUD操作。

  2. 实现泛型Repository类,使用EF Core执行数据库操作。

  3. 根据需要,为特定实体定义扩展的Repository接口和实现。

此外,我们还可以使用Unit of Work模式来管理多个Repository,确保在同一个事务中提交所有更改。

下面我们逐步构建一个示例:

1. 定义泛型IRepository接口

csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;

public interface IRepository<TEntity> where TEntity : class
{
    Task<TEntity> GetByIdAsync(int id);
    Task<IEnumerable<TEntity>> GetAllAsync();
    Task<IEnumerable<TEntity>> FindAsync(Expression<Func<TEntity, bool>> predicate);
    Task<TEntity> SingleOrDefaultAsync(Expression<Func<TEntity, bool>> predicate);
    Task AddAsync(TEntity entity);
    Task AddRangeAsync(IEnumerable<TEntity> entities);
    void Remove(TEntity entity);
    void RemoveRange(IEnumerable<TEntity> entities);
    void Update(TEntity entity);
}

2. 实现泛型Repository类

csharp
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
    protected readonly DbContext Context;
    protected readonly DbSet<TEntity> DbSet;

    public Repository(DbContext context)
    {
        Context = context;
        DbSet = context.Set<TEntity>();
    }

    public virtual async Task<TEntity> GetByIdAsync(int id)
    {
        return await DbSet.FindAsync(id);
    }

    public virtual async Task<IEnumerable<TEntity>> GetAllAsync()
    {
        return await DbSet.ToListAsync();
    }

    public virtual async Task<IEnumerable<TEntity>> FindAsync(Expression<Func<TEntity, bool>> predicate)
    {
        return await DbSet.Where(predicate).ToListAsync();
    }

    public virtual async Task<TEntity> SingleOrDefaultAsync(Expression<Func<TEntity, bool>> predicate)
    {
        return await DbSet.SingleOrDefaultAsync(predicate);
    }

    public virtual async Task AddAsync(TEntity entity)
    {
        await DbSet.AddAsync(entity);
    }

    public virtual async Task AddRangeAsync(IEnumerable<TEntity> entities)
    {
        await DbSet.AddRangeAsync(entities);
    }

    public virtual void Remove(TEntity entity)
    {
        DbSet.Remove(entity);
    }

    public virtual void RemoveRange(IEnumerable<TEntity> entities)
    {
        DbSet.RemoveRange(entities);
    }

    public virtual void Update(TEntity entity)
    {
        DbSet.Update(entity);
    }
}

3. 定义Unit of Work接口和实现

Unit of Work模式用于将多个Repository放在一个单元中,共享同一个DbContext,并统一提交更改。

csharp
using System;
using System.Threading.Tasks;

public interface IUnitOfWork : IDisposable
{
    IRepository<TEntity> GetRepository<TEntity>() where TEntity : class;
    Task<int> SaveChangesAsync();
}

public class UnitOfWork : IUnitOfWork
{
    private readonly DbContext _context;
    private readonly Dictionary<Type, object> _repositories;

    public UnitOfWork(DbContext context)
    {
        _context = context;
        _repositories = new Dictionary<Type, object>();
    }

    public IRepository<TEntity> GetRepository<TEntity>() where TEntity : class
    {
        if (_repositories.ContainsKey(typeof(TEntity)))
        {
            return (IRepository<TEntity>)_repositories[typeof(TEntity)];
        }

        var repository = new Repository<TEntity>(_context);
        _repositories.Add(typeof(TEntity), repository);
        return repository;
    }

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

    public void Dispose()
    {
        _context?.Dispose();
    }
}

4. 在Startup或Program中注册

在.NET Core中,我们需要在依赖注入容器中注册DbContext和UnitOfWork。

csharp
// 在Startup.cs的ConfigureServices方法中,或者使用.NET 6的Program.cs
services.AddDbContext<MyDbContext>(options =>
    options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

services.AddScoped<IUnitOfWork, UnitOfWork>();

5. 使用Repository和Unit of Work

在控制器或服务中,通过构造函数注入IUnitOfWork,然后通过IUnitOfWork获取特定实体的Repository。

csharp
public class ProductService
{
    private readonly IUnitOfWork _unitOfWork;

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

    public async Task<IEnumerable<Product>> GetProductsAsync()
    {
        var productRepository = _unitOfWork.GetRepository<Product>();
        return await productRepository.GetAllAsync();
    }

    public async Task AddProductAsync(Product product)
    {
        var productRepository = _unitOfWork.GetRepository<Product>();
        await productRepository.AddAsync(product);
        await _unitOfWork.SaveChangesAsync();
    }

    // 其他方法...
}

6. 特定实体的自定义Repository

如果某个实体需要特定的查询方法,可以创建一个特定的接口,继承自IRepository,并添加额外的方法。

csharp
public interface IProductRepository : IRepository<Product>
{
    Task<IEnumerable<Product>> GetProductsByCategoryAsync(string category);
}

public class ProductRepository : Repository<Product>, IProductRepository
{
    public ProductRepository(MyDbContext context) : base(context)
    {
    }

    public async Task<IEnumerable<Product>> GetProductsByCategoryAsync(string category)
    {
        return await Context.Set<Product>().Where(p => p.Category == category).ToListAsync();
    }
}

然后,在Unit of Work中,我们需要为IProductRepository提供相应的获取方法。这可能需要修改UnitOfWork类,为每个特定的Repository提供特定的属性或方法。

总结

以上是一个常见的Repository模式在.NET Core EF中的用法。它提供了对常见CRUD操作的抽象,并且通过Unit of Work管理事务。同时,通过泛型接口和实现,减少了重复代码。对于特定需求,可以通过创建特定的Repository接口和实现来扩展。

注意:在实际项目中,需要根据具体需求调整。例如,可能需要对查询进行优化,使用分页、排序等。此外,上述示例中的Repository是异步的,这符合现代编程的最佳实践。

另外,有些开发者认为在EF Core中,DbContext本身已经实现了Repository和Unit of Work模式,因此再封装一层有时会被认为是过度设计。所以,是否采用这种模式取决于项目的复杂性和团队的选择。

 

在.NET Core EF中使用Repository模式是一种常见的数据访问抽象方式。下面我将详细介绍其一般用法和如何覆盖常见操作。

1. 基础Repository接口设计

csharp
public interface IRepository<T> where T : class
{
    // 查询操作
    Task<T> GetByIdAsync(int id);
    Task<T> GetByIdAsync(string id);
    Task<IEnumerable<T>> GetAllAsync();
    IQueryable<T> GetQueryable();
    
    // 条件查询
    Task<IEnumerable<T>> GetAsync(Expression<Func<T, bool>> predicate);
    Task<T> FirstOrDefaultAsync(Expression<Func<T, bool>> predicate);
    Task<bool> AnyAsync(Expression<Func<T, bool>> predicate);
    
    // 分页查询
    Task<(IEnumerable<T> Items, int TotalCount)> GetPagedAsync(
        Expression<Func<T, bool>> predicate, 
        int pageIndex, 
        int pageSize,
        Expression<Func<T, object>> orderBy = null,
        bool isDescending = false);
    
    // 添加操作
    Task AddAsync(T entity);
    Task AddRangeAsync(IEnumerable<T> entities);
    
    // 更新操作
    void Update(T entity);
    void UpdateRange(IEnumerable<T> entities);
    
    // 删除操作
    void Remove(T entity);
    void RemoveRange(IEnumerable<T> entities);
    
    // 统计
    Task<int> CountAsync();
    Task<int> CountAsync(Expression<Func<T, bool>> predicate);
}

2. 泛型Repository实现

csharp
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 virtual async Task<T> GetByIdAsync(int id)
    {
        return await _dbSet.FindAsync(id);
    }

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

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

    public virtual IQueryable<T> GetQueryable()
    {
        return _dbSet.AsQueryable();
    }

    // 条件查询
    public virtual async Task<IEnumerable<T>> GetAsync(Expression<Func<T, bool>> predicate)
    {
        return await _dbSet.Where(predicate).ToListAsync();
    }

    public virtual async Task<T> FirstOrDefaultAsync(Expression<Func<T, bool>> predicate)
    {
        return await _dbSet.FirstOrDefaultAsync(predicate);
    }

    public virtual async Task<bool> AnyAsync(Expression<Func<T, bool>> predicate)
    {
        return await _dbSet.AnyAsync(predicate);
    }

    // 分页查询
    public virtual async Task<(IEnumerable<T> Items, int TotalCount)> GetPagedAsync(
        Expression<Func<T, bool>> predicate, 
        int pageIndex, 
        int pageSize,
        Expression<Func<T, object>> orderBy = null,
        bool isDescending = false)
    {
        var query = _dbSet.AsQueryable();
        
        if (predicate != null)
            query = query.Where(predicate);

        var totalCount = await query.CountAsync();

        if (orderBy != null)
        {
            query = isDescending ? 
                query.OrderByDescending(orderBy) : 
                query.OrderBy(orderBy);
        }

        var items = await query
            .Skip((pageIndex - 1) * pageSize)
            .Take(pageSize)
            .ToListAsync();

        return (items, totalCount);
    }

    // 添加操作
    public virtual async Task AddAsync(T entity)
    {
        await _dbSet.AddAsync(entity);
    }

    public virtual async Task AddRangeAsync(IEnumerable<T> entities)
    {
        await _dbSet.AddRangeAsync(entities);
    }

    // 更新操作
    public virtual void Update(T entity)
    {
        _dbSet.Update(entity);
    }

    public virtual void UpdateRange(IEnumerable<T> entities)
    {
        _dbSet.UpdateRange(entities);
    }

    // 删除操作
    public virtual void Remove(T entity)
    {
        _dbSet.Remove(entity);
    }

    public virtual void RemoveRange(IEnumerable<T> entities)
    {
        _dbSet.RemoveRange(entities);
    }

    // 统计
    public virtual async Task<int> CountAsync()
    {
        return await _dbSet.CountAsync();
    }

    public virtual async Task<int> CountAsync(Expression<Func<T, bool>> predicate)
    {
        return await _dbSet.CountAsync(predicate);
    }
}

3. 特定实体Repository扩展

csharp
public interface IUserRepository : IRepository<User>
{
    // 特定于User的查询方法
    Task<User> GetByEmailAsync(string email);
    Task<IEnumerable<User>> GetActiveUsersAsync();
    Task<bool> IsEmailUniqueAsync(string email, int? excludeUserId = null);
}

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

    public async Task<User> GetByEmailAsync(string email)
    {
        return await _dbSet.FirstOrDefaultAsync(u => u.Email == email);
    }

    public async Task<IEnumerable<User>> GetActiveUsersAsync()
    {
        return await _dbSet
            .Where(u => u.IsActive && !u.IsDeleted)
            .OrderBy(u => u.CreatedDate)
            .ToListAsync();
    }

    public async Task<bool> IsEmailUniqueAsync(string email, int? excludeUserId = null)
    {
        var query = _dbSet.Where(u => u.Email == email);
        
        if (excludeUserId.HasValue)
            query = query.Where(u => u.Id != excludeUserId.Value);

        return !await query.AnyAsync();
    }
}

4. Unit of Work模式

csharp
public interface IUnitOfWork : IDisposable
{
    IRepository<T> GetRepository<T>() where T : class;
    IUserRepository Users { get; }
    // 其他特定Repository
    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;
        Users = new UserRepository(context);
    }

    public IUserRepository Users { get; }

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

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

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

    public async Task CommitTransactionAsync()
    {
        try
        {
            await _context.SaveChangesAsync();
            await _transaction.CommitAsync();
        }
        finally
        {
            await _transaction.DisposeAsync();
        }
    }

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

    public void Dispose()
    {
        _context?.Dispose();
        _transaction?.Dispose();
    }
}

5. 依赖注入配置

csharp
// Program.cs 或 Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    // 注册泛型Repository
    services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
    
    // 注册特定Repository
    services.AddScoped<IUserRepository, UserRepository>();
    
    // 注册Unit of Work
    services.AddScoped<IUnitOfWork, UnitOfWork>();
}

6. 在Service层中使用

csharp
public class UserService
{
    private readonly IUnitOfWork _unitOfWork;

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

    public async Task<UserDto> CreateUserAsync(CreateUserRequest request)
    {
        // 使用事务
        await _unitOfWork.BeginTransactionAsync();
        
        try
        {
            // 检查邮箱是否唯一
            if (!await _unitOfWork.Users.IsEmailUniqueAsync(request.Email))
            {
                throw new InvalidOperationException("Email already exists");
            }

            var user = new User
            {
                Email = request.Email,
                Name = request.Name,
                CreatedDate = DateTime.UtcNow
            };

            await _unitOfWork.Users.AddAsync(user);
            await _unitOfWork.SaveChangesAsync();
            await _unitOfWork.CommitTransactionAsync();

            return MapToDto(user);
        }
        catch
        {
            await _unitOfWork.RollbackTransactionAsync();
            throw;
        }
    }

    public async Task<PagedResult<UserDto>> GetUsersAsync(int page, int pageSize)
    {
        var (users, totalCount) = await _unitOfWork.Users.GetPagedAsync(
            predicate: null,
            pageIndex: page,
            pageSize: pageSize,
            orderBy: u => u.CreatedDate,
            isDescending: true
        );

        return new PagedResult<UserDto>
        {
            Items = users.Select(MapToDto),
            TotalCount = totalCount,
            Page = page,
            PageSize = pageSize
        };
    }

    private UserDto MapToDto(User user) => new()
    {
        Id = user.Id,
        Email = user.Email,
        Name = user.Name
    };
}

7. 高级功能扩展

规约模式

csharp
// 规约接口
public interface ISpecification<T>
{
    Expression<Func<T, bool>> Criteria { get; }
    List<Expression<Func<T, object>>> Includes { get; }
    Expression<Func<T, object>> OrderBy { get; }
    Expression<Func<T, object>> OrderByDescending { get; }
}

// 在Repository中添加规约支持
public virtual async Task<IEnumerable<T>> GetAsync(ISpecification<T> spec)
{
    return await ApplySpecification(spec).ToListAsync();
}

private IQueryable<T> ApplySpecification(ISpecification<T> spec)
{
    var query = _dbSet.AsQueryable();
    
    if (spec.Criteria != null)
        query = query.Where(spec.Criteria);
    
    if (spec.Includes != null)
        query = spec.Includes.Aggregate(query, (current, include) => current.Include(include));
    
    if (spec.OrderBy != null)
        query = query.OrderBy(spec.OrderBy);
    else if (spec.OrderByDescending != null)
        query = query.OrderByDescending(spec.OrderByDescending);
    
    return query;
}

总结

这样的Repository设计覆盖了以下常见操作:

  • 基本CRUD:增删改查

  • 查询功能:条件查询、分页、排序、统计

  • 事务支持:Unit of Work模式

  • 扩展性:支持特定实体的自定义查询

  • 性能优化:使用IQueryable延迟执行

  • 异步操作:全面支持异步编程

这种设计提供了良好的抽象,使数据访问层与业务逻辑层分离,同时保持了EF Core的灵活性和强大功能。