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

EF6 Core中如何将IEnumerable<T>动态投影到动态对象?

动态生成Linq Select子句的解决方案

问题分析

你遇到的问题主要源于三个核心错误:

  1. Linq方法误用:SelectMany是用来扁平化嵌套集合的,而投影(选择指定列)需要用Select方法。
  2. 字符串无法被Linq解析:"new({formattedColumns})"是普通字符串,Linq(尤其是Linq to Entities/SQL)需要的是表达式树而非字面量,所以这段代码不会产生任何投影效果,自然返回所有列。
  3. 泛型约束缺失:硬编码new { c.Id, c.Name }时,编译器不知道泛型参数T包含Id、Name属性,因此报错。

解决方案

下面提供两种可行方案,按需选择:


方案一:使用第三方库(简单高效,推荐)

借助System.Linq.Dynamic.Core库,它支持用字符串语法构建动态Linq查询,内部会自动将字符串转换为表达式树。

  1. 安装NuGet包:
Install-Package System.Linq.Dynamic.Core
  1. 修改你的方法:
using System.Linq.Dynamic.Core;

public static IQueryable<dynamic> AddSelectClause<T>(SearchDto criteria, IQueryable<T> queryable, ControllerStateManager stateManager)
{
    if (criteria?.SelectColumns != null && criteria.SelectColumns.Any())
    {
        var columnString = string.Join(", ", criteria.SelectColumns);
        // 动态生成Select子句,返回动态类型
        return queryable.Select($"new({columnString})");
    }

    // 未指定列时返回空结构的动态类型
    return queryable.Select("new()");
}

使用示例:

var result = AddSelectClause(criteria, query, stateManager);
foreach (dynamic item in result)
{
    Console.WriteLine(item.Id); // 直接访问选择的列属性
}

方案二:手动构建表达式树(无第三方依赖)

如果不想引入外部库,可以手动构建表达式树实现动态投影,返回ExpandoObject(动态对象):

using System.Linq.Expressions;
using System.Reflection;
using System.Dynamic;

public static IQueryable<ExpandoObject> AddSelectClause<T>(SearchDto criteria, IQueryable<T> queryable, ControllerStateManager stateManager)
{
    if (criteria?.SelectColumns == null || !criteria.SelectColumns.Any())
    {
        return queryable.Select(_ => new ExpandoObject());
    }

    var entityType = typeof(T);
    // 过滤出实体中存在的有效属性
    var validProperties = criteria.SelectColumns
        .Select(col => entityType.GetProperty(col, BindingFlags.Public | BindingFlags.Instance))
        .Where(prop => prop != null)
        .ToList();

    if (!validProperties.Any())
    {
        return queryable.Select(_ => new ExpandoObject());
    }

    // 构建参数表达式:x => ...
    var parameter = Expression.Parameter(entityType, "x");
    // 创建ExpandoObject变量并初始化
    var expandoVar = Expression.Variable(typeof(ExpandoObject), "expando");
    var assignExpando = Expression.Assign(expandoVar, Expression.New(typeof(ExpandoObject)));
    // 获取IDictionary<string, object>的Add方法
    var addMethod = typeof(IDictionary<string, object>).GetMethod("Add")!;

    // 构建每个属性的Add调用逻辑
    var addExpressions = validProperties.Select(prop =>
    {
        var propertyValue = Expression.MakeMemberAccess(parameter, prop);
        var boxedValue = Expression.Convert(propertyValue, typeof(object));
        return Expression.Call(
            Expression.Convert(expandoVar, typeof(IDictionary<string, object>)),
            addMethod,
            Expression.Constant(prop.Name),
            boxedValue
        );
    });

    // 组合所有表达式:初始化ExpandoObject → 添加属性 → 返回对象
    var block = Expression.Block(
        new[] { expandoVar },
        assignExpando,
        Expression.Block(addExpressions),
        expandoVar
    );

    // 构建Lambda表达式并应用Select
    var selector = Expression.Lambda<Func<T, ExpandoObject>>(block, parameter);
    return queryable.Select(selector);
}

使用方式和方案一一致,通过动态类型直接访问属性即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 05:55:16