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

如何通过反射基于ChildAttribute构建指定的属性Where过滤表达式?

动态构建带过滤的属性访问表达式(基于反射和特性)

要实现通过反射构建和你手动写的完全一致的表达式,咱们可以通过反射读取特性信息 + 表达式树构建来完成,下面是具体的实现步骤和代码:

1. 先确认基础定义(确保特性与实体类可用)

首先得保证你的ChildAttribute和相关类的定义没问题,比如:

public enum FilterType
{
    Equal
}

public class ChildAttribute : Attribute
{
    public FilterType FilterType { get; }
    public string PropertyName { get; }
    public object Value { get; }

    public ChildAttribute(FilterType filterType, string propertyName, object value)
    {
        FilterType = filterType;
        PropertyName = propertyName;
        Value = value;
    }
}

public class BaseEntity { }
public class ItemFileLink 
{
    public int ParentTypeId { get; set; }
    // 其他业务属性...
}

2. 核心实现:反射+表达式树构建目标表达式

咱们一步步拆解实现逻辑:

步骤1:获取目标属性与特性信息

先通过反射找到AircraftFlight类里的Files属性,再读取它上面的ChildAttribute配置:

// 目标实体类型
Type entityType = typeof(AircraftFlight);
// 反射获取Files属性
PropertyInfo filesProperty = entityType.GetProperty(nameof(AircraftFlight.Files)) 
    ?? throw new InvalidOperationException("未找到Files属性");
// 获取属性上绑定的ChildAttribute
ChildAttribute childAttr = filesProperty.GetCustomAttribute<ChildAttribute>() 
    ?? throw new InvalidOperationException("Files属性未标记ChildAttribute");

步骤2:拼接表达式树的各个节点

接下来我们要手动拼接出i => i.Files.Where(f => f.ParentTypeId == 13)这个表达式的每一部分:

① 创建实体参数(对应表达式里的i)

ParameterExpression entityParam = Expression.Parameter(entityType, "i");

② 构建属性访问表达式(对应i.Files)

MemberExpression filesAccessExpr = Expression.Property(entityParam, filesProperty);

③ 构建Where方法的过滤Lambda(对应f => f.ParentTypeId == 13)

先处理集合元素的过滤逻辑:

// 获取集合元素类型(这里是ItemFileLink)
Type elementType = typeof(ItemFileLink);
// 创建过滤Lambda的参数(对应`f`)
ParameterExpression elementParam = Expression.Parameter(elementType, "f");

// 构建`f.ParentTypeId`的属性访问表达式
MemberExpression filterPropertyExpr = Expression.Property(elementParam, childAttr.PropertyName);
// 构建常量值(确保类型与属性匹配)
ConstantExpression filterValueExpr = Expression.Constant(
    Convert.ChangeType(childAttr.Value, filterPropertyExpr.Type), 
    filterPropertyExpr.Type);
// 构建相等判断表达式(对应`f.ParentTypeId == 13`)
BinaryExpression filterConditionExpr = Expression.Equal(filterPropertyExpr, filterValueExpr);

// 把条件包装成完整的Lambda表达式
Expression<Func<ItemFileLink, bool>> filterLambda = 
    Expression.Lambda<Func<ItemFileLink, bool>>(filterConditionExpr, elementParam);

④ 构建Where方法的调用表达式

因为Where是Enumerable类的扩展方法,需要反射获取方法定义并构建调用:

// 获取Enumerable.Where的泛型方法模板
MethodInfo whereMethodDef = typeof(Enumerable).GetMethods()
    .First(m => m.Name == nameof(Enumerable.Where) && m.GetParameters().Length == 2);
// 实例化泛型方法(指定元素类型)
MethodInfo whereMethod = whereMethodDef.MakeGenericMethod(elementType);

// 构建Where方法调用:Files.Where(filterLambda)
MethodCallExpression whereCallExpr = Expression.Call(
    whereMethod, 
    filesAccessExpr, 
    filterLambda);

⑤ 包装成最终的目标表达式

// 最终得到和手动编写完全一致的表达式
Expression<Func<AircraftFlight, IEnumerable<ItemFileLink>>> finalExpression = 
    Expression.Lambda<Func<AircraftFlight, IEnumerable<ItemFileLink>>>(whereCallExpr, entityParam);

3. 验证效果

你可以编译表达式并执行,验证和手动写的逻辑是否一致:

// 编译成可执行委托
var func = finalExpression.Compile();
// 测试数据
var testFlight = new AircraftFlight 
{
    Files = new List<ItemFileLink> 
    {
        new ItemFileLink { ParentTypeId = 13 },
        new ItemFileLink { ParentTypeId = 14 }
    }
};
// 执行过滤
var filteredFiles = func(testFlight);
// 结果应该只包含ParentTypeId=13的元素

额外优化建议

  • 如果你的FilterType有更多类型(比如大于、小于),可以扩展条件表达式的构建逻辑(比如用Expression.GreaterThan等)
  • 可以把这段逻辑封装成通用方法,支持任意实体类型和带有ChildAttribute的属性
  • 增加类型安全检查,避免特性配置的属性名或值类型不匹配的问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:00:31