动态构建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
相关产品推荐
相关产品推荐

