使用C# Expressions为Entity Framework构建Array.All表达式
动态构建Linq表达式实现指定WHERE子句逻辑
实体类定义
public class Report { public string Year { get; set; } public ICollection<Documents>? Documents { get; set; } } public class Documents { public string Year { get; set; } public Report? Report { get; set; } public string Type { get; set; } }
目标Linq查询逻辑
需要实现的查询逻辑等价于:
query.Where(x => x.Report.Documents.All(c => (c.Type == "R" || c.Type == "RX") && c.Year == x.Report.Year))
现有待完善代码
private Expression GetExpression(Expression memberExpression) { Expression<Func<ICollection<Documents>, bool>> containsExpr = (ICollection<Documents> q) => q.All((Func<Documents, bool> )null); var containsMethod = (containsExpr.Body as MethodCallExpression).Method; Expression<Func<Documents, bool>> contains = x => x.Type =="R"; MemberExpression collectionPropertyAccessor = Expression.Property(memberExpression, "Documents"); var targetCodesContains = Expression.Call(containsMethod, collectionPropertyAccessor, contains); return binaryExpression = Expression.And(targetCodesContains, targetCodesContains); }
完善后的代码
private Expression GetExpression(Expression memberExpression) { // 获取Enumerable.All的泛型方法定义 var allMethodDefinition = typeof(Enumerable).GetMethods() .Where(m => m.Name == "All" && m.GetParameters().Length == 2) .First() .MakeGenericMethod(typeof(Documents)); // 构建Documents类型的参数c var docParam = Expression.Parameter(typeof(Documents), "c"); // 构建c.Type == "R" || c.Type == "RX"的表达式 var typeR = Expression.Equal(Expression.Property(docParam, "Type"), Expression.Constant("R")); var typeRX = Expression.Equal(Expression.Property(docParam, "Type"), Expression.Constant("RX")); var typeCondition = Expression.OrElse(typeR, typeRX); // 构建c.Year == x.Report.Year的表达式(memberExpression对应x.Report) var reportYear = Expression.Property(memberExpression, "Year"); var docYear = Expression.Property(docParam, "Year"); var yearCondition = Expression.Equal(docYear, reportYear); // 合并条件生成All方法的谓词Lambda var allPredicate = Expression.Lambda<Func<Documents, bool>>( Expression.AndAlso(typeCondition, yearCondition), docParam); // 获取x.Report.Documents属性表达式 var documentsCollection = Expression.Property(memberExpression, "Documents"); // 生成All方法调用表达式 return Expression.Call(allMethodDefinition, documentsCollection, allPredicate); }
关键修改说明
- 严谨获取All方法:替换原代码中通过表达式树提取方法的方式,直接从
Enumerable类获取泛型方法定义,避免类型匹配异常。 - 构建完整谓词逻辑:
- 创建
Documents类型的参数c,对应Lambda表达式的输入参数。 - 分别实现类型判断和年份匹配的子表达式,再用
AndAlso合并为完整条件。
- 创建
- 移除无效逻辑:删除原代码中无意义的重复
Expression.And调用,替换为实际的All方法调用表达式。
内容的提问来源于stack exchange,提问作者dawid debinski
相关产品推荐
相关产品推荐

