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

动态构建LINQ表达式时Regex.IsMatch引发EF无法翻译的问题

动态LINQ表达式结合Regex查询的EF Core翻译问题

问题背景

我参考Jeremy的动态LINQ实现做了小幅修改,想要用Regex评估查询谓词,生成的Expression.Lambda表达式如下:

.Where(d => new List<string> { "P1101", "P1102"}
    .Contains(d.PntName) | Regex.IsMatch(
    input: d.PntName, 
    pattern: "^A.*"))

运行时触发EF Core翻译错误:

The LINQ expression 'DbSet
.Where(d => List { "P1101", "P1102", }
.Contains(d.PntName) | Regex.IsMatch(
input: d.PntName,
pattern: "^A.*"))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

更新疑问

查询中涉及Regex的部分是运行时动态生成的,我通过以下代码获取MethodInfo:

typeof(Regex).GetMethods(BindingFlags.Static | BindingFlags.Public)
     .Single(m => m.Name == nameof(Regex.IsMatch)
          && m.GetParameters().Length == 2); 

想问能不能让这个方法调用省略参数名?

完整代码

Program.cs

using (var context = new DataTransactionContext(optionsWithLog))
{
    var count = await context.DbDataTransactions.CountAsync();
    Console.WriteLine($"Verified insert count: {count}.");
    Console.WriteLine("Parsing expression...");
    var parser = new JsonExpressionParser();
    var predicate = parser.ParseExpressionOf<DataTransaction>(
        JsonDocument.Parse(
            await File.ReadAllTextAsync("dataDatabaseRules.json")));
    Console.WriteLine("Retrieving from database...");
    var query = context.DbDataTransactions.Where(predicate);
        //.OrderBy(t => t.PntNo);
    var results = await query.ToListAsync();
    Console.WriteLine($"Retrieved {results.Count}");
    Console.WriteLine("Sample:");
    Console.WriteLine(results[0]);
}

ExpressionParser.cs

public class JsonExpressionParser
{
    private const string StringStr = "string";

    private readonly string BooleanStr = nameof(Boolean).ToLower();
    private readonly string Number = nameof(Number).ToLower();
    private readonly string In = nameof(In).ToLower();
    private readonly string And = nameof(And).ToLower();
    private readonly string Contains = nameof(Contains).ToLower();

    //方法构造
    private readonly MethodInfo MethodContains = typeof(Enumerable).GetMethods(
                    BindingFlags.Static | BindingFlags.Public)
                    .Single(m => m.Name == nameof(Enumerable.Contains)
                        && m.GetParameters().Length == 2);

    private readonly MethodInfo PerformMyMeth =
    //    typeof(Regex).GetMethod("IsMatch", new Type[] { typeof(string) });
    typeof(Regex).GetMethods(BindingFlags.Static | BindingFlags.Public)
                .Single(m => m.Name == nameof(Regex.IsMatch)
                    && m.GetParameters().Length == 2); //设置Regex的参数数量

    private delegate Expression Binder(Expression left, Expression right);

    private Expression ParseTree<T>(
        JsonElement condition,
        ParameterExpression parm)
    {
        Expression left = null;

        //处理AND/OR逻辑
        var gate = condition.GetProperty(nameof(condition)).GetString();
        
        JsonElement rules = condition.GetProperty(nameof(rules));

        //Binder 简写为Binary Expression
        Binder binder = gate == And ? (Binder)Expression.And : Expression.Or;

        Expression bind(Expression left, Expression right) =>
            left == null ? right : binder(left, right);

        foreach (var rule in rules.EnumerateArray())
        {
            if (rule.TryGetProperty(nameof(condition), out JsonElement check))
            {
                var right = ParseTree<T>(rule, parm);
                left = bind(left, right);
                continue;
            }

            string @operator = rule.GetProperty(nameof(@operator)).GetString();     //例如 IN
            string type = rule.GetProperty(nameof(type)).GetString();               //例如 string
            string field = rule.GetProperty(nameof(field)).GetString();             //例如 PntName
            
            JsonElement value = rule.GetProperty(nameof(value));                    //匹配值
            
            //表达式常量需要和属性比较,以下代码创建属性表达式
            var property = Expression.Property(parm, field);
            
            if (@operator == In)
            {
                //使用之前构建的Contains方法
                var contains = MethodContains.MakeGenericMethod(typeof(string));

                object val = value.EnumerateArray().Select(e => e.GetString())
                    .ToList();

                var right = Expression.Call(
                    contains,
                    Expression.Constant(val),
                    property);
                left = bind(left, right);
            }
            else if(@operator == Contains)
            {
                object val = (type == StringStr || type == BooleanStr) ?
                    (object)value.GetString() : value.GetDecimal();

                var toCompare = Expression.Constant(val);
                var right = Expression.Call(PerformMyMeth, property, toCompare);

                left = bind(left, right);
            }
            else
            {
                object val = (type == StringStr || type == BooleanStr) ?
                    (object)value.GetString() : value.GetDecimal();
                var toCompare = Expression.Constant(val);
                var right = Expression.Equal(property, toCompare);

                left = bind(left, right);
            }
        }

        return left;
    }

    public Expression<Func<T, bool>> ParseExpressionOf<T>(JsonDocument doc)
    {
        //通过静态工厂方法创建Expression
        // ParameterExpression,Lambda表达式的参数引用
        var itemExpression = Expression.Parameter(typeof(T));

        Console.WriteLine("Item expression: " + itemExpression.ToString());

        var conditions = ParseTree<T>(doc.RootElement, itemExpression);

        if (conditions.CanReduce)
        {
            conditions = conditions.ReduceAndCheck();
        }

        Console.WriteLine("Conditions: " + conditions.ToString());

        var query = Expression.Lambda<Func<T, bool>>(conditions, itemExpression);
        return query;
    }

    public Func<T, bool> ParsePredicateOf<T>(JsonDocument doc)
    {
        var query = ParseExpressionOf<T>(doc);
        return query.Compile();
    }
}

内容的提问来源于stack exchange,提问作者K N

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 22:37:05