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

自定义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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 23:45:26