[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模式实现示例:
-
定义泛型Repository接口,包含常见的CRUD操作。
-
实现泛型Repository类,使用EF Core执行数据库操作。
-
根据需要,为特定实体定义扩展的Repository接口和实现。
此外,我们还可以使用Unit of Work模式来管理多个Repository,确保在同一个事务中提交所有更改。
下面我们逐步构建一个示例:
1. 定义泛型IRepository接口
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类
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,并统一提交更改。
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。
// 在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。
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,并添加额外的方法。
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接口设计
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实现
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扩展
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模式
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. 依赖注入配置
// 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层中使用
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. 高级功能扩展
规约模式
// 规约接口
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的灵活性和强大功能。