如何解决C#泛型抽象类方法中Predicate作为Find过滤参数的报错问题
异常原因
你遇到的是MongoDB .NET驱动的典型表达式解析错误:Find方法接收的是Expression<Func<M, bool>>类型的表达式树,驱动需要将整个表达式翻译为MongoDB原生查询语法。你传入的Predicate<M>是普通委托类型,直接在表达式中调用condition(M)会在表达式树中生成无法被驱动识别的Invoke委托调用节点,最终抛出不支持过滤器的异常。
你之前尝试的(condition ?? (ignore => true))(M)只是处理了空委托的默认值问题,本质还是在表达式树中插入了委托调用,所以不会生效。
可行解决方案
方案1:表达式树合并(推荐,服务端过滤,性能最优)
将参数类型从Predicate<M>改为Expression<Func<M, bool>>,通过表达式树访问器拼接两个查询条件,让驱动可以完整解析整个查询。
第一步:定义表达式拼接辅助类
// 表达式替换访问器,用于合并两个表达式的参数 internal class ReplaceExpressionVisitor : ExpressionVisitor { private readonly Expression _oldParam; private readonly Expression _newParam; public ReplaceExpressionVisitor(Expression oldParam, Expression newParam) { _oldParam = oldParam; _newParam = newParam; } public override Expression Visit(Expression node) { return ReferenceEquals(node, _oldParam) ? _newParam : base.Visit(node); } } // 扩展方法,拼接两个And关系的表达式 public static Expression<Func<T, bool>> AndAlso<T>( this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right) { if (left == null) return right; if (right == null) return left; var newParam = Expression.Parameter(typeof(T), "x"); var newLeft = new ReplaceExpressionVisitor(left.Parameters[0], newParam).Visit(left.Body); var newRight = new ReplaceExpressionVisitor(right.Parameters[0], newParam).Visit(right.Body); return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(newLeft, newRight), newParam); }
第二步:修改Get方法
public I Get<I>(string id, IBsonSerializer<I> serializer = null, Expression<Func<M, bool>> condition = null) where I : IModelBase { var baseFilter = (Expression<Func<M, bool>>)(m => m.Id == id); var finalFilter = baseFilter.AndAlso(condition); return Items.Find(finalFilter).As(serializer).FirstOrDefault(); }
方案2:客户端内存过滤(仅适合小数据量场景)
如果你必须保留Predicate<M>参数类型,可以先按Id查询出结果到内存,再用委托过滤,缺点是无法利用MongoDB的索引优化,查询性能受匹配结果数量影响。
public I Get<I>(string id, IBsonSerializer<I> serializer = null, Predicate<M> condition = null) where I : IModelBase { // 先按Id过滤出结果到内存 var idMatchResult = Items.Find(m => m.Id == id).ToList(); // 内存中执行委托过滤 var target = idMatchResult.FirstOrDefault(m => condition == null || condition(m)); return target == null ? default : (I)Convert.ChangeType(target, typeof(I)); }
内容的提问来源于stack exchange,提问作者Jan Safronov
相关产品推荐
相关产品推荐

