【问题标题】:Trying to create a morethan, equal or greaterthan dynamic filter for dates in linq尝试为 linq 中的日期创建大于、等于或大于动态过滤器
【发布时间】:2019-07-17 17:51:26
【问题描述】:

我一直在尝试为 Linq 创建一个表达式树过滤器,它采用 2 个日期和一串可能的值 {“lessthan”、“equals”、“morethan”}。我希望将调用格式化为 Query.Where(CompareDates(x => x.left, right, "less than"));

我有代码:

        public static IQueryable<TSource> CompareDates<TSource>(
            this IQueryable<TSource> source, 
            Expression<Func<TSource, DateTime?>> left, 
            DateTime? right, 
            string equality)
        {
            if (right == null || string.IsNullOrWhiteSpace(equality))
                return source;

            var p = left.Parameters.Single();
            Expression member = p;

            Expression leftExpression = Expression.Property(member, "left");
            Expression rightParameter = Expression.Constant(right, typeof(DateTime));

            BinaryExpression BExpression = null;
            switch (equality)
            {
                case "lessthan":
                    BExpression = Expression.LessThan(leftExpression, rightParameter);
                    break;
                case "equal":
                    BExpression = Expression.Equal(leftExpression, rightParameter);
                    break;
                case "morethan":
                    BExpression = Expression.GreaterThan(leftExpression, rightParameter);
                    break;
                default:
                    throw new Exception(String.Format("Equality {0} not recognised.", equality));
            }

            return source.Where(Expression.Lambda<Func<TSource, bool>>(BExpression, p));
        }

不幸的是,它在 SARRestAPI.Extensions 的 System.Linq.Expressions.Expression.Property(Expression expression, String propertyName) 中产生“System.ArgumentException:实例属性 'left' 未定义为类型 'Model' 的错误。 Expressions.CompareDates[TSource](IQueryable1 source, Expression1 src,提供的日期时间,字符串相等)"

有人知道为什么会这样吗?

【问题讨论】:

  • 作为一个不相关的说明,我建议使用枚举而不是字符串作为相等参数
  • 好的,所以memberParameterExpression 中的ParameterExpression,即TSource。听起来TSource 这里是Model,而你要求.left (通过Expression.Property(member, "left")) - 所以... 确实 Model 有一个.left财产?我们可以在这里看到Model 吗? .left 有可能是一个 field 吗?
  • 顺便说一句,IMO 你应该在这里合并树木......让我看看我能做什么

标签: c# linq lambda


【解决方案1】:

我们来了;你想要做的是使用传入选择器的.Body,而不是寻找.left。意思是,给定一个x =&gt; x.Foo.Bar.Blap 的输入选择器、一个常量和一个比较,您想通过重用 x (参数,你是已经在做)和身体 (x.Foo.Bar.Blap)。

在代码中(注意这适用于任何TValue,而不仅仅是DateTime):

public enum Comparison
{
    Equal,
    NotEqual,
    LessThan,
    LessThanOrEqual,
    GreaterThan,
    GreaterThanOrEqual
}
public static IQueryable<TSource> Compare<TSource, TValue>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, TValue>> selector,
        TValue value,
        Comparison comparison)
{
    Expression left = selector.Body;
    Expression right = Expression.Constant(value, typeof(TValue));

    BinaryExpression body;
    switch (comparison)
    {
        case Comparison.LessThan:
            body = Expression.LessThan(left, right);
            break;
        case Comparison.LessThanOrEqual:
            body = Expression.LessThanOrEqual(left, right);
            break;
        case Comparison.Equal:
            body = Expression.Equal(left, right);
            break;
        case Comparison.NotEqual:
            body = Expression.NotEqual(left, right);
            break;
        case Comparison.GreaterThan:
            body = Expression.GreaterThan(left, right);
            break;
        case Comparison.GreaterThanOrEqual:
            body = Expression.GreaterThanOrEqual(left, right);
            break;
        default:
            throw new ArgumentOutOfRangeException(nameof(comparison));
    }

    return source.Where(Expression.Lambda<Func<TSource, bool>>(body, selector.Parameters));
}

示例用法(此处使用 LINQ-to-Objects,但它也适用于其他 LINQ 后端):

var arr = new[] { new { X = 11 }, new { X = 12 }, new { X = 13 }, new { X = 14 } };
var source = arr.AsQueryable();

var filtered = source.Compare(x => x.X, 12, Comparison.GreaterThan);
foreach (var item in filtered)
{
    Console.WriteLine(item.X); // 13 and 14
}

请注意,使用 C# vCurrent 你可以这样做:

var body = comparison switch
{
    Comparison.LessThan => Expression.LessThan(left, right),
    Comparison.LessThanOrEqual => Expression.LessThanOrEqual(left, right),
    Comparison.Equal => Expression.Equal(left, right),
    Comparison.NotEqual => Expression.NotEqual(left, right),
    Comparison.GreaterThan => Expression.GreaterThan(left, right),
    Comparison.GreaterThanOrEqual => Expression.GreaterThanOrEqual(left, right),
    _ => throw new ArgumentOutOfRangeException(nameof(comparison)),
};
return source.Where(Expression.Lambda<Func<TSource, bool>>(body, selector.Parameters));

你可能会更喜欢。

【讨论】:

    猜你喜欢
    • 2016-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-07-30
    • 2021-07-01
    相关资源
    最近更新 更多