C# Expression Tree调用带参嵌套Expression报参数不匹配问题求解
问题根因
- 第一种把自定义参数作为表达式工厂方法普通入参的写法报错,核心原因有两个:一是在XPTO1内部嵌套调用XPTO2时没有传入myVariable参数;二是直接将XPTO2返回的表达式对象传入Select,没有经过LinqExpander的展开逻辑统一绑定参数实例,嵌套替换时出现参数实例不匹配,触发参数计数错误。
- 第二种把myVariable加入表达式参数列表的写法编译失败,是因为
Queryable.Select的投影表达式仅支持1个输入参数(即当前查询序列的元素),传入双参数的Expression<Func<TEntity,short, TPropertyResult>>不符合方法的泛型签名约束,编译器会直接拦截。
正确实现方案
保持所有投影表达式为Expression<Func<TEntity, TPropertyResult>>的单参数签名,额外参数作为表达式工厂方法的普通入参传递,嵌套调用时依赖LinqExpander的特性标记和Invoke展开逻辑统一处理参数绑定,具体实现如下:
- 保留ViewModel2的原有实现,不需要修改:
public static Expression<Func<TEntity, TPropertyResult>> XPTO2(short myVariable) { return x => x.navigationProperty.MyVariableProperty == null || ( ViewModel2Field1 = (x.someValue == myVariable) ? true : false ) } [ReplaceWithExpression(MethodName = nameof(XPTO2))] public static TPropertyResult FromEntity(TEntity entity, short myVariable) { return XPTO2(myVariable).Compile().Invoke(entity); }
- 修改ViewModel1的XPTO1实现,嵌套调用时直接调用标记了
ReplaceWithExpression特性的FromEntity方法,传入当前myVariable参数,同时对内层集合调用AsExpandable()支持子表达式展开:
public static Expression<Func<TEntity, TPropertyResult>> XPTO1(short myVariable) { return x => x.navigationProperty.MyVariableProperty == null ? null : new TPropertyResult() { ViewModel1FieldViewModel2 = x.DataModel2Field1.AsQueryable() .AsExpandable() .Select(d2Entity => RegistryViewModel.FromEntity(d2Entity, myVariable)) .ToList() } } public static TPropertyResult FromEntity(TEntity entity, short myVariable) { return XPTO1(myVariable).Compile().Invoke(entity); }
- 服务层调用时直接给XPTO1工厂方法传入myVariable参数即可,不需要给Select传额外参数:
var x = _myRepository.MyFunc(servicoId) .AsExpandable() .Select(ViewModel1.XPTO1(myVariable)) .ToQueryString();
注意事项
- 不要将自定义业务参数加入表达式树的Func参数列表,这类参数作为工厂方法的普通入参传入时,LinqExpander会自动将其解析为常量/参数值,展开时不会出现绑定问题。
- 嵌套调用其他模块的投影逻辑时,优先调用标记了
ReplaceWithExpression特性的普通方法,不要直接调用返回Expression的工厂方法再传入Select,否则会丢失参数绑定上下文,触发参数匹配错误。 - 内层集合使用投影前必须调用
AsExpandable(),否则LinqExpander无法识别内层的标记方法,无法完成表达式替换。
内容的提问来源于stack exchange,提问作者AimForNuts
相关产品推荐
相关产品推荐

