AutoMapper表达式转换异常:参数类型不匹配问题求助
问题原因
你遇到的异常核心是:AutoMapper默认无法正确转换表达式树中的参数类型。虽然你配置了Expression<Func<ProductEntity, bool>>到Expression<Func<tblProduct, bool>>的映射,但AutoMapper并没有遍历表达式树替换参数的类型,导致生成的Lambda表达式依然使用ProductEntity作为参数类型,而Repository的Where方法期望的是tblProduct类型的参数,最终触发类型不匹配异常。
从调试视图也能看出问题:Lambda的委托声明是Func<tblProduct, bool>,但参数却是BusinessEntities.ProductEntity $x,类型完全不匹配。
解决方案:自定义ExpressionVisitor替换参数类型
要解决这个问题,需要手动遍历表达式树,将所有原参数类型(ProductEntity)的ParameterExpression替换为目标类型(tblProduct)的参数,同时确保成员访问(比如x.Price)能正确映射到目标实体的对应属性。
1. 基础版:属性名一致时的参数替换
如果ProductEntity和tblProduct的属性名完全一致(比如都有Price属性),可以使用以下简单的ExpressionVisitor实现:
public class ParameterReplacer : ExpressionVisitor { private readonly ParameterExpression _oldParam; private readonly ParameterExpression _newParam; public ParameterReplacer(ParameterExpression oldParam, ParameterExpression newParam) { _oldParam = oldParam; _newParam = newParam; } protected override Expression VisitParameter(ParameterExpression node) { // 替换原参数为目标类型参数 return node == _oldParam ? _newParam : base.VisitParameter(node); } } // 扩展方法简化调用 public static class ExpressionExtensions { public static Expression<Func<TTarget, bool>> ConvertToTargetParam<TSource, TTarget>(this Expression<Func<TSource, bool>> sourceExpr) { var targetParam = Expression.Parameter(typeof(TTarget), sourceExpr.Parameters[0].Name); var replacer = new ParameterReplacer(sourceExpr.Parameters[0], targetParam); var newBody = replacer.Visit(sourceExpr.Body); return Expression.Lambda<Func<TTarget, bool>>(newBody, targetParam); } }
使用方式:
// 假设query是Expression<Func<ProductEntity, bool>>类型 var convertedExpr = query.ConvertToTargetParam<ProductEntity, tblProduct>(); // 传入Repository方法执行 var result = repository.GetWithInclude(convertedExpr).ToList();
2. 进阶版:结合AutoMapper处理属性名不一致
如果ProductEntity和tblProduct的属性名不一致(比如ProductEntity的ProductPrice对应tblProduct的Price),需要结合AutoMapper的映射配置,在遍历表达式树时替换成员访问的属性:
public class ExpressionMapperVisitor : ExpressionVisitor { private readonly ParameterExpression _targetParam; private readonly IConfigurationProvider _mapperConfig; private readonly Type _sourceType; private readonly Type _targetType; public ExpressionMapperVisitor(ParameterExpression targetParam, IConfigurationProvider mapperConfig, Type sourceType, Type targetType) { _targetParam = targetParam; _mapperConfig = mapperConfig; _sourceType = sourceType; _targetType = targetType; } protected override Expression VisitParameter(ParameterExpression node) { // 替换原类型参数为目标类型参数 return node.Type == _sourceType ? _targetParam : base.VisitParameter(node); } protected override Expression VisitMember(MemberExpression node) { if (node.Expression.Type == _sourceType) { // 从AutoMapper配置中获取属性映射关系 var typeMap = _mapperConfig.FindTypeMapFor(_sourceType, _targetType); if (typeMap != null) { var propertyMap = typeMap.GetPropertyMapFor(new MemberInfoWrapper(node.Member)); if (propertyMap != null) { // 替换为目标实体的对应属性 var visitedExpr = Visit(node.Expression); return Expression.MakeMemberAccess(visitedExpr, propertyMap.DestinationMember); } } } return base.VisitMember(node); } // 辅助类:用于AutoMapper查找属性映射 private class MemberInfoWrapper : IMemberInfo { private readonly MemberInfo _member; public MemberInfoWrapper(MemberInfo member) => _member = member; public Type MemberType => _member.MemberType; public string Name => _member.Name; public Type DeclaringType => _member.DeclaringType; public MemberInfo MemberInfo => _member; } } // 扩展方法 public static class ExpressionExtensions { public static Expression<Func<TTarget, bool>> MapExpression<TSource, TTarget>(this Expression<Func<TSource, bool>> sourceExpr, IMapper mapper) { var targetParam = Expression.Parameter(typeof(TTarget), sourceExpr.Parameters[0].Name); var visitor = new ExpressionMapperVisitor(targetParam, mapper.ConfigurationProvider, typeof(TSource), typeof(TTarget)); var newBody = visitor.Visit(sourceExpr.Body); return Expression.Lambda<Func<TTarget, bool>>(newBody, targetParam); } }
使用方式:
首先确保AutoMapper配置了实体间的属性映射:
var mapperConfig = new MapperConfiguration(cfg => { cfg.CreateMap<ProductEntity, tblProduct>() .ForMember(dest => dest.Price, opt => opt.MapFrom(src => src.ProductPrice)); // 示例:属性名不一致时的映射 cfg.CreateMap<tblProduct, ProductEntity>(); }); var mapper = mapperConfig.CreateMapper();
然后转换表达式:
var convertedExpr = query.MapExpression<ProductEntity, tblProduct>(mapper); var result = repository.GetWithInclude(convertedExpr).ToList();
为什么AutoMapper直接映射表达式不行?
AutoMapper的核心设计是用于对象实例的映射,对表达式树的支持非常有限,它不会自动遍历并修改表达式树中的参数类型和成员访问节点,因此无法直接完成这种跨类型的表达式转换需求。
内容的提问来源于stack exchange,提问作者Asif Aziz

