我正在使用以下函数从 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 个) 表达式不要太复杂
该问题的文档链接,这对我的案例来说不是很有用
如何拆分表达式
我建议您忽略此“可维护性”警告。根据 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);
但老实说,我不认为这会使代码更具可读性或可维护性。
模型小提琴这里。