减少表达式<Func<T,bool>谓词

问题描述 投票:0回答:1

我正在使用以下函数从 mongoDB 获取数据:

Task<IEnumerable<T>> ReadDocuments(Expression<Func<T,bool>> predicate)

我需要根据多种条件过滤数据,并且我正在使用如下功能:

cars = await this.carRepository.ReadDocuments(car => car.no == no && 
car.engine == engine && car.model == model && car.color == color && car.country==country)

对于上面的代码,我面临 SonarQube 问题:

减少表达式中使用的条件运算符数量 (4)(最多允许 3 个) 表达式不要太复杂

该问题的文档链接,这对我的案例来说不是很有用

如何拆分表达式谓词中的条件以减少语句中条件运算符的数量?

c# .net sonarqube .net-6.0
1个回答
0
投票

我建议您忽略此“可维护性”警告。根据 docs,SonarQube 建议通过将部分提取到方法中来简化复杂的条件,例如:

if (((condition1 && condition2) || (condition3 && condition4)) && condition5) { ... }

会变成:

if ((MyFirstCondition() || MySecondCondition()) && MyLastCondition()) { ... }

但是,您不能对传递给 LINQ 数据库提供程序的查询表达式采用此解决方案,因为数据库提供程序通常无法将任意

Expression.Invoke
Expression.Call
.NET 方法调用转换为数据库查询。参见例如

作为解决方法,我想您可以创建一些扩展方法,使用

AndAlso
表达式组合多个谓词表达式:

public static class ExpressionExtensions
{
    public static  Expression<Func<T,bool>> AndAlsoAll<T>(this Expression<Func<T,bool>> expression, params Expression<Func<T,bool>> [] others)
    {
        var body = expression.Body;
        var parameter = expression.Parameters[0];
        foreach (var other in others)
            body = Expression.AndAlso(body, new ParameterReplacer((other.Parameters[0], (Expression)parameter)).Visit(other.Body));
        return Expression.Lambda<Func<T, bool>>(body, parameter);
    }
}

class ParameterReplacer : ExpressionVisitor
{
    // Replace formal parameters (e.g. of a lambda body) with some containing expression in scope.
    readonly Dictionary<ParameterExpression, Expression> parametersToReplace;
    public ParameterReplacer(params (ParameterExpression parameter, Expression replacement) [] parametersToReplace) =>
        this.parametersToReplace = parametersToReplace.ToDictionary(p => p.parameter, p => p.replacement);
    protected override Expression VisitParameter(ParameterExpression p) => 
        parametersToReplace.TryGetValue(p, out var e) ? e : base.VisitParameter(p);
}

然后向您的

carRepository
类型添加一些方法(或扩展方法),例如:

public Task<IEnumerable<T>> ReadDocumentsWhereAll(Expression<Func<T,bool>> predicate, params Expression<Func<T,bool>> [] andAlsoAllPredicates)
{
    return ReadDocuments(predicate.AndAlsoAll(andAlsoAllPredicates));
}

现在您将能够执行以下操作:

var cars = await this.carRepository.ReadDocumentsWhereAll(car => car.no == no, 
                                                          car => car.engine == engine, 
                                                          car => car.model == model, 
                                                          car => car.color == color, 
                                                          car => car.country==country);

但老实说,我不认为这会使代码更具可读性或可维护性。

模型小提琴这里

© www.soinside.com 2019 - 2024. All rights reserved.