You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

EF 6迁移至EF Core:如何重构Include表达式以支持ThenInclude?

问题

将基于Entity Framework的.NET Framework项目迁移至.NET 8时,EF 6中通过表达式数组传递Include语句的方式在EF Core中抛出异常。

EF 6中的Include表达式数组示例:

Expression<Func<Company, object>>[] getCompaniesIncludeExpressions =
{
    e => e.Categories.Select(c => c.Projects.Select(d => d.Jobs.Select(f => f.Location))),
    e => e.CompanyUsers
}; 

通用查询方法实现:

public async Task<List<TEntity>> SelectAllAsync(Expression<Func<TEntity, object>>[] includeExpressions, Expression<Func<TEntity, bool>> whereExpression)
{
    var entities = Context.Set<TEntity>().AsQueryable();

    if (includeExpressions != null)
    {
        foreach (var includeExpression in includeExpressions)
        {
            entities = entities.Include(includeExpression);
        }
    }

    if (whereExpression == null) return await entities.ToListAsync();

    var list = await entities
        .Where(whereExpression).ToListAsync();

    return list;
}

EF Core运行时抛出的异常信息:

AggregateException: One or more errors occurred. (The expression 'e.Categories.AsQueryable().Select(c => c.Projects.AsQueryable().Select(d => d.Jobs.AsQueryable().Select(f => f.Location)))' is invalid inside an 'Include' operation, since it does not represent a property access: 't => t.MyProperty'. To target navigations declared on derived types, use casting ('t => ((Derived)t).MyProperty') or the 'as' operator ('t => (t as Derived).MyProperty'). Collection navigation access can be filtered by composing Where, OrderBy(Descending), ThenBy(Descending), Skip or Take operations.

需重构代码以支持传递表达式并通过ThenInclude包含子属性。

解决方案

方案1:用Func<IQueryable<TEntity>, IQueryable<TEntity>>封装Include链式调用

这是最贴合EF Core设计的方案,直接将Include和ThenInclude的逻辑封装为委托:

1. 修改通用查询方法

更新SelectAllAsync,接收包含Include逻辑的委托数组:

public async Task<List<TEntity>> SelectAllAsync(Func<IQueryable<TEntity>, IQueryable<TEntity>>[] includeActions, Expression<Func<TEntity, bool>> whereExpression)
{
    var entities = Context.Set<TEntity>().AsQueryable();

    if (includeActions != null)
    {
        foreach (var includeAction in includeActions)
        {
            entities = includeAction(entities);
        }
    }

    if (whereExpression != null)
    {
        entities = entities.Where(whereExpression);
    }

    return await entities.ToListAsync();
}

2. 定义包含多层导航的委托数组

针对Company实体,编写完整的Include+ThenInclude逻辑:

Func<IQueryable<Company>, IQueryable<Company>>[] getCompaniesIncludeActions =
{
    query => query.Include(c => c.Categories)
                  .ThenInclude(cat => cat.Projects)
                  .ThenInclude(proj => proj.Jobs)
                  .ThenInclude(job => job.Location),
    query => query.Include(c => c.CompanyUsers)
};

3. 调用通用方法

var companies = await SelectAllAsync(getCompaniesIncludeActions, null);

方案2:过渡方案——使用字符串导航路径

如果需要最小化修改原有代码结构,可以改用字符串形式的导航路径(注意:硬编码字符串易出错,仅作过渡用):

1. 修改通用方法接收字符串数组

public async Task<List<TEntity>> SelectAllAsync(string[] includePaths, Expression<Func<TEntity, bool>> whereExpression)
{
    var entities = Context.Set<TEntity>().AsQueryable();

    if (includePaths != null)
    {
        foreach (var path in includePaths)
        {
            entities = entities.Include(path);
        }
    }

    if (whereExpression != null)
    {
        entities = entities.Where(whereExpression);
    }

    return await entities.ToListAsync();
}

2. 调用时传入导航路径

string[] getCompaniesIncludePaths =
{
    "Categories.Projects.Jobs.Location",
    "CompanyUsers"
};

var companies = await SelectAllAsync(getCompaniesIncludePaths, null);

核心差异说明

EF Core的Include要求表达式必须是直接的属性访问,不允许通过Select嵌套表示多层包含——这是和EF6的核心区别。EF6支持用Select嵌套简化多层包含写法,但EF Core强制要求通过ThenInclude明确链式调用下一级导航属性,以此保证查询逻辑的清晰度和执行效率。

内容的提问来源于stack exchange,提问作者ledragon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 04:10:04