EF Core泛型仓储如何无字符串方式包含嵌套导航属性?
解决方案
方案1:修改仓储方法接受包含逻辑委托
把原有的GetAllIncluding方法参数改为接受构建包含逻辑的委托,直接利用EF Core的Include和ThenInclude链式调用,全程强类型且完美支持嵌套导航属性。
修改后的仓储方法:
public async Task<IEnumerable<T>> GetAllIncluding(params Func<IQueryable<T>, IQueryable<T>>[] includeActions) { IQueryable<T> query = dbSet; foreach (var includeAction in includeActions) { query = includeAction(query); } return await query.ToListAsync(); }
调用方式:
public async Task<IEnumerable<SomeEntity>> GetAllDetailsAsync() { var data = await unitOfWork.Service.GetAllIncluding( q => q.Include(x => x.DirectNavigation1), q => q.Include(x => x.DirectNavigation2), q => q.Include(x => x.DirectNavigation3).ThenInclude(o => o.NestedNavigation1) ); }
方案2:简化查询入口支持链式调用
如果想让调用更贴合EF Core的原生写法,可以给仓储增加基础查询入口,或者改造GetAll方法接受查询修饰委托:
// 新增基础查询入口 public IQueryable<T> Query() { return dbSet; } // 或者改造GetAll方法 public async Task<IEnumerable<T>> GetAll(Func<IQueryable<T>, IQueryable<T>> queryModifier = null) { var query = dbSet.AsQueryable(); if (queryModifier != null) { query = queryModifier(query); } return await query.ToListAsync(); }
调用时直接链式添加Include和ThenInclude:
public async Task<IEnumerable<SomeEntity>> GetAllDetailsAsync() { // 使用Query入口的方式 var data = await unitOfWork.Service.Query() .Include(x => x.DirectNavigation1) .Include(x => x.DirectNavigation2) .Include(x => x.DirectNavigation3).ThenInclude(o => o.NestedNavigation1) .ToListAsync(); // 或者使用改造后的GetAll方法 var data = await unitOfWork.Service.GetAll(query => query.Include(x => x.DirectNavigation1) .Include(x => x.DirectNavigation2) .Include(x => x.DirectNavigation3).ThenInclude(o => o.NestedNavigation1) ); }
方案3:解析表达式兼容原有调用格式
如果坚持保留原有的params Expression<Func<T, object>>[]参数形式,可以通过解析表达式处理嵌套的Select逻辑,不过这种方式需要额外的表达式解析代码,健壮性不如前两种方案:
public async Task<IEnumerable<T>> GetAllIncluding(params Expression<Func<T, object>>[] includes) { IQueryable<T> query = dbSet; foreach (var include in includes) { var memberExpr = include.Body as MemberExpression; if (memberExpr != null) { // 处理直接导航属性 query = query.Include(include); } else { // 解析嵌套Select表达式,提取导航路径 var methodCallExpr = include.Body as MethodCallExpression; if (methodCallExpr != null && methodCallExpr.Method.Name == "Select") { var outerNavName = ((MemberExpression)methodCallExpr.Arguments[0]).Member.Name; var innerNavName = ((MemberExpression)((LambdaExpression)methodCallExpr.Arguments[1]).Body).Member.Name; query = query.Include($"{outerNavName}.{innerNavName}"); } } } return await query.ToListAsync(); }
这种方式可以兼容原有的直接导航调用,同时支持x => x.DirectNavigation3.Select(o => o.NestedNavigation1)的写法,但需要根据实际使用的导航类型调整解析逻辑。
内容的提问来源于stack exchange,提问作者M.Bouabdallah
相关产品推荐
相关产品推荐

