不久前,我发布了问题Trying to create a morethan, equal or greaterthan dynamic filter for dates in linq,该问题很有帮助,但是我现在正尝试添加另一张支票,以使lambda现在看起来像这样
(left, right, equality, isCompleteCheck) => IsCompleteCheck == false | left equality right && IsCompleteCheck == true
我写了这段代码:
public static IQueryable<TSource> FilterCompleteDateComparison<TSource>(
this IQueryable<TSource> source,
Expression<Func<TSource, DateTime?>> left,
DateTime? right,
string equality,
Expression<Func<TSource, bool>> isCompleteCheck)
{
// CombinedExpression
// FirstExpression Comparison Expression SecondExpression
// (left, right, equality, isCompleteCheck) => IsCompleteCheck == false | left equality right && IsCompleteCheck == true
if (right == null || string.IsNullOrWhiteSpace(equality))
return source;
Expression IsCompleteCheckExpression = isCompleteCheck.Body;
BinaryExpression FirstExpression = Expression.Equal(IsCompleteCheckExpression, Expression.Constant(false, typeof(bool)));
Expression LeftExpression = left.Body;
Expression RightParameter = Expression.Constant(right, typeof(DateTime?));
BinaryExpression ComparisonExpression = null;
switch (equality)
{
case "lessthan":
ComparisonExpression = Expression.LessThan(LeftExpression, RightParameter);
break;
case "equal":
ComparisonExpression = Expression.Equal(LeftExpression, RightParameter);
break;
case "morethan":
ComparisonExpression = Expression.GreaterThan(LeftExpression, RightParameter);
break;
default:
throw new Exception(String.Format("Equality {0} not recognised.", equality));
}
BinaryExpression SecondExpression = Expression.Equal(IsCompleteCheckExpression, Expression.Constant(true, typeof(bool)));
BinaryExpression CombinedExpression = Expression.Or(FirstExpression, Expression.And(ComparisonExpression, SecondExpression));
return source.Where(Expression.Lambda<Func<TSource, bool>>(CombinedExpression, left.Parameters));
}
可以被
调用BaseQuery.FilterCompleteDateComparison<SessionModel>(x => x.StartDate, DateValidated.Date, dateEquality, y => y.IsComplete);
但是如何为lambda提供IsCompleteCheckExpression
参数?我试图将IsCompleteCheckExpression
参数简单地连接到leftexpression
参数上,并将其提供给lambda,但是错误表明属性太多。
有人有什么想法吗?
答案 0 :(得分:1)
您需要使用一个ExpressionVisitor
,可以将isCompleteCheck
中的lambda参数更改为left
中的lambda参数,以便将其用于两个lambda求值。通常将其命名为Replace
:
public static class ExpressionExt {
/// <summary>
/// Replaces an Expression (reference Equals) with another Expression
/// </summary>
/// <param name="orig">The original Expression.</param>
/// <param name="from">The from Expression.</param>
/// <param name="to">The to Expression.</param>
/// <returns>Expression with all occurrences of from replaced with to</returns>
public static Expression Replace(this Expression orig, Expression from, Expression to) => new ReplaceVisitor(from, to).Visit(orig);
}
/// <summary>
/// ExpressionVisitor to replace an Expression (that is Equals) with another Expression.
/// </summary>
public class ReplaceVisitor : ExpressionVisitor {
readonly Expression from;
readonly Expression to;
public ReplaceVisitor(Expression from, Expression to) {
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node) => node == from ? to : base.Visit(node);
}
鉴于此,对方法的更改非常简单。我还简化了您的测试逻辑(我的一般规则说,永远不要对布尔值进行测试,并且要进行DRY,因此t1 == false || t2 && t1 == true
应该为!t1 || t2
)。我还使用了Dictionary
而不是switch
来制作更简洁/更干燥且易于扩展的比较表达式。
static Dictionary<string, Func<Expression, Expression, BinaryExpression>> ComparisonOps = new Dictionary<string, Func<Expression, Expression, BinaryExpression>> {
{ "lessthan", Expression.LessThan },
{ "equal", Expression.Equal },
{ "morethan", Expression.GreaterThan },
};
public static IQueryable<TSource> FilterCompleteDateComparison<TSource>(
this IQueryable<TSource> source,
Expression<Func<TSource, DateTime?>> left,
DateTime? right,
string equality,
Expression<Func<TSource, bool>> isCompleteCheck) {
// CombinedExpression
// FirstExpression Comparison Expression
// (left, right, equality, isCompleteCheck) => s => !IsCompleteCheck(s) || left(s) equality right
if (right == null || string.IsNullOrWhiteSpace(equality))
return source;
var IsCompleteCheckExpression = isCompleteCheck.Body.Replace(isCompleteCheck.Parameters[0], left.Parameters[0]);
var FirstExpression = Expression.Not(IsCompleteCheckExpression);
var LeftExpression = left.Body;
var RightParameter = Expression.Constant(right, typeof(DateTime?));
if (!ComparisonOps.TryGetValue(equality, out var op))
throw new Exception(String.Format("Equality {0} not recognised.", equality));
var ComparisonExpression = op(LeftExpression, RightParameter);
var CombinedExpression = Expression.Or(FirstExpression, ComparisonExpression);
return source.Where(Expression.Lambda<Func<TSource, bool>>(CombinedExpression, left.Parameters));
}