C#中如何使用带Include与表达式的GenericRepository?
泛型仓储模式中GetAllIncludingAsync方法的Include参数调用问题
问题说明
已实现泛型仓储模式,定义了IGenericRepository接口及对应的GenericRepository实现类,但在调用GetAllIncludingAsync方法时,不清楚如何传入Include参数来关联查询Person实体。
现有代码
IGenericRepository接口
public interface IGenericRepository<T> where T : class { Task<IEnumerable<T>> GetAllIncludingAsync(Expression<Func<T, bool>> expression, Expression<Func<object, bool>>[] includes, CancellationToken cancellationToken = default); Task<T?> GetIncludingAsync(Expression<Func<T, bool>> expression, Expression<Func<T, object>>[] includes, CancellationToken cancellationToken = default); }
GenericRepository实现类
public class GenericRepository<T> : IGenericRepository<T> where T : class { protected readonly MyDbContext _dbContext; private readonly DbSet<T> _entitySet; public GenericRepository(MyDbContext dbContext) { _dbContext = dbContext; _entitySet = _dbContext.Set<T>(); } public async Task<IEnumerable<T>> GetAllIncludingAsync(Expression<Func<T, bool>> expression, Expression<Func<object, bool>>[] includes, CancellationToken cancellationToken = default) { IQueryable<T> query = _dbContext.Set<T>(); foreach (var item in includes) { query = (IQueryable<T>)query.Include(item); } return await query.ToListAsync(); } public async Task<T?> GetIncludingAsync(Expression<Func<T, bool>> expression, Expression<Func<T, object>>[] includes, CancellationToken cancellationToken = default) { IQueryable<T> query = _dbContext.Set<T>(); foreach (var item in includes) { query = query.Include(item); } return await query.FirstOrDefaultAsync(expression); } }
当前调用代码
IEnumerable<Booking>? bookings = await _unitOfWork .BookingRepository .GetAllIncludingAsync(x => x.Id == personId, y => y.Inc); //here I want to include Person
问题修正与正确调用方式
第一步:修正接口参数类型与实现缺陷
GetAllIncludingAsync方法的includes参数类型定义错误,需和GetIncludingAsync保持一致,改为Expression<Func<T, object>>[],否则会引发类型转换异常。同时原实现遗漏了过滤条件的应用,需补充Where逻辑:
修正后的接口方法
Task<IEnumerable<T>> GetAllIncludingAsync(Expression<Func<T, bool>> expression, Expression<Func<T, object>>[] includes, CancellationToken cancellationToken = default);
修正后的实现类方法
public async Task<IEnumerable<T>> GetAllIncludingAsync(Expression<Func<T, bool>> expression, Expression<Func<T, object>>[] includes, CancellationToken cancellationToken = default) { IQueryable<T> query = _dbContext.Set<T>(); foreach (var item in includes) { query = query.Include(item); } // 应用传入的过滤条件 query = query.Where(expression); return await query.ToListAsync(cancellationToken); }
第二步:正确传入Include参数
假设Booking实体包含名为Person的导航属性,调用时传入表示该导航属性的表达式数组即可:
// 关联单个导航属性的情况 IEnumerable<Booking>? bookings = await _unitOfWork .BookingRepository .GetAllIncludingAsync( x => x.Id == personId, new[] { x => x.Person }, // 指定要关联的Person导航属性 CancellationToken.None);
如果需要同时关联多个导航属性,直接在数组中添加对应表达式:
// 关联多个导航属性的情况 IEnumerable<Booking>? bookings = await _unitOfWork .BookingRepository .GetAllIncludingAsync( x => x.Id == personId, new[] { x => x.Person, x => x.Room }, // 同时关联Person和Room CancellationToken.None);
额外优化建议
- 给
includes参数设置默认值null,无需关联查询时可省略该参数:
实现类中需增加空值判断:Task<IEnumerable<T>> GetAllIncludingAsync(Expression<Func<T, bool>> expression, Expression<Func<T, object>>[]? includes = null, CancellationToken cancellationToken = default);if (includes != null) { foreach (var item in includes) { query = query.Include(item); } } - 若需要支持多层级导航属性(如
Booking.Person.Addresses),可扩展方法支持ThenInclude逻辑。
内容的提问来源于stack exchange,提问作者user20291208
相关产品推荐
相关产品推荐

