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

如何解决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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 02:45:10