自定义LINQ表达式转换查询数据库实体时抛出异常
问题:LINQ表达式转换适配实体类时抛出异常
尝试使用基于业务类Person的LINQ查询过滤数据库实体类PersonEntity,已实现表达式转换逻辑但运行时抛出异常,无法定位原因。Person与PersonEntity结构几乎一致(仅PersonEntity多了Id字段)。
测试代码
using System.Linq.Expressions; namespace ConsoleApp1 { class Person { public int Age { get; set; } public string? Name { get; set; } } class PersonEntity { public int Id { get; set; } public int Age { get; set; } public string? Name { get; set; } } static class ExpressionTranslator { public static Expression<Func<PersonEntity, bool>> Translate(Expression<Func<Person, bool>> expression) { ParameterExpression parameter = Expression.Parameter(typeof(PersonEntity), "e"); Expression body = new ReplaceVisitor(expression.Parameters[0], parameter).Visit(expression.Body); return Expression.Lambda<Func<PersonEntity, bool>>(body, parameter); } private class ReplaceVisitor : ExpressionVisitor { private readonly ParameterExpression _oldParameter; private readonly ParameterExpression _newParameter; public ReplaceVisitor(ParameterExpression oldParameter, ParameterExpression newParameter) { _oldParameter = oldParameter; _newParameter = newParameter; } protected override Expression VisitParameter(ParameterExpression node) { return node == _oldParameter ? _newParameter : base.VisitParameter(node); } } } internal class Program { static void Main(string[] args) { Expression<Func<Person, bool>> actualQuery = x => x.Age > 18; IEnumerable<Person> result = QueryPersons(actualQuery); foreach (var p in result) { Console.WriteLine($"{p.Name}, {p.Age}"); } } private static IEnumerable<Person> QueryPersons(Expression<Func<Person, bool>> query) { var db = new List<PersonEntity>(new[] { new PersonEntity() { Id= 1, Age=15, Name="John" }, new PersonEntity() { Id= 2, Age=25, Name="Sally" }, new PersonEntity() { Id= 3, Age=35, Name="Jack" }, }); Expression<Func<PersonEntity, bool>> entityQuery = ExpressionTranslator.Translate(query); return db.AsQueryable().Where(entityQuery).Select(x => new Person() { Age = x.Age, Name = x.Name }).ToList(); } } }
异常原因分析
当前的ReplaceVisitor仅替换了表达式中的参数实例,但没有处理成员访问表达式的类型匹配问题。例如原表达式x => x.Age > 18中,x.Age是访问Person类型的Age成员,替换参数为PersonEntity类型后,成员访问仍然指向Person.Age,而非PersonEntity.Age,导致运行时抛出ArgumentException:“实例类型与成员类型不匹配”。
解决方法
修改ReplaceVisitor,重写VisitMember方法,当访问的成员属于旧参数类型时,自动映射到新参数类型的同名成员:
private class ReplaceVisitor : ExpressionVisitor { private readonly ParameterExpression _oldParameter; private readonly ParameterExpression _newParameter; public ReplaceVisitor(ParameterExpression oldParameter, ParameterExpression newParameter) { _oldParameter = oldParameter; _newParameter = newParameter; } protected override Expression VisitParameter(ParameterExpression node) { return node == _oldParameter ? _newParameter : base.VisitParameter(node); } protected override Expression VisitMember(MemberExpression node) { // 仅当成员属于旧参数类型时,替换为新参数类型的同名成员 if (node.Member.DeclaringType == _oldParameter.Type && node.Expression == _oldParameter) { var newMember = _newParameter.Type.GetMember(node.Member.Name).FirstOrDefault(); if (newMember != null) { return Expression.MakeMemberAccess(_newParameter, newMember); } } return base.VisitMember(node); } }
修改说明
- 新增
VisitMember重写逻辑,检查当前访问的成员是否属于旧参数类型(Person),且表达式节点是旧参数实例 - 若匹配,则从新参数类型(
PersonEntity)中获取同名成员,构建新的成员访问表达式 - 确保替换后的表达式完全适配
PersonEntity类型,解决类型不匹配问题
内容的提问来源于stack exchange,提问作者Michael Rall
相关产品推荐
相关产品推荐

