如何在C#中将Expression<Func<A, bool>>转换为Expression<Func<B, bool>>
在C#中转换Expression<Func<A, bool>>到Expression<Func<B, bool>>的实现方法
要实现无共同父类的A、B两类之间的表达式属性映射,核心是通过**自定义ExpressionVisitor**重写表达式的成员访问逻辑,将针对A类属性的访问替换为B类对应的属性访问。以下是具体实现步骤:
1. 定义属性映射关系
首先明确A和B的属性对应规则,用字典存储映射关系:
var propertyMap = new Dictionary<string, string> { { nameof(A.AProperty1), nameof(B.BProperty1) }, { nameof(A.AProperty2), nameof(B.BProperty2) } };
2. 自定义ExpressionVisitor实现替换逻辑
继承ExpressionVisitor,重写两个关键方法:
VisitParameter:将原表达式中的A类型参数替换为B类型参数VisitMember:将A类的属性访问替换为B类对应的属性访问
public class PropertyMappingVisitor<TSource, TTarget> : ExpressionVisitor { private readonly Dictionary<string, string> _propertyMap; private readonly ParameterExpression _targetParameter; public PropertyMappingVisitor(Dictionary<string, string> propertyMap, ParameterExpression targetParameter) { _propertyMap = propertyMap; _targetParameter = targetParameter; } // 替换参数类型:从TSource换成TTarget protected override Expression VisitParameter(ParameterExpression node) { return node.Type == typeof(TSource) ? _targetParameter : base.VisitParameter(node); } // 替换属性访问:将TSource的属性换成TTarget对应属性 protected override Expression VisitMember(MemberExpression node) { // 仅处理TSource类型的属性访问 if (node.Expression is ParameterExpression param && param.Type == typeof(TSource)) { if (_propertyMap.TryGetValue(node.Member.Name, out var targetPropertyName)) { // 获取TTarget的对应属性 var targetProperty = typeof(TTarget).GetProperty(targetPropertyName); if (targetProperty != null) { // 构建新的属性访问表达式 return Expression.Property(_targetParameter, targetProperty); } } } return base.VisitMember(node); } }
3. 封装表达式转换方法
写一个工具方法,将Expression<Func<TSource, bool>>转换为Expression<Func<TTarget, bool>>:
public static class ExpressionConverter { public static Expression<Func<TTarget, bool>> Convert<TSource, TTarget>( Expression<Func<TSource, bool>> sourceExpr, Dictionary<string, string> propertyMap) { // 创建TTarget类型的参数表达式 var targetParam = Expression.Parameter(typeof(TTarget), "p"); var visitor = new PropertyMappingVisitor<TSource, TTarget>(propertyMap, targetParam); // 遍历并替换原表达式的所有节点 var convertedBody = visitor.Visit(sourceExpr.Body); // 构建新的表达式 return Expression.Lambda<Func<TTarget, bool>>(convertedBody, targetParam); } }
4. 在业务代码中使用转换
修改你的FindAsync方法,将传入的A类型表达式转换为B类型后再用于查询:
public async Task<B> FindAsync(Expression<Func<A, bool>> where, CancellationToken cancellationToken = default) { // 定义属性映射 var propertyMap = new Dictionary<string, string> { { nameof(A.AProperty1), nameof(B.BProperty1) }, { nameof(A.AProperty2), nameof(B.BProperty2) } }; // 转换表达式 var convertedWhere = ExpressionConverter.Convert<A, B>(where, propertyMap); // 使用转换后的表达式查询B类数据 return await _dbContext.B.Where(convertedWhere).SingleOrDefaultAsync(cancellationToken); }
验证转换效果
针对示例表达式:
Expression<Func<A, bool>> where = p => p.AProperty1 == "x" && p.AProperty2 == "2";
转换后会得到等价的B类表达式:
Expression<Func<B, bool>> result = p => p.BProperty1 == "x" && p.BProperty2 == "2";
内容的提问来源于stack exchange,提问作者Szyszka947
相关产品推荐
相关产品推荐

