通用 IQueryable 到 CompareDate

Generic IQueryable to CompareDate

所以我正在尝试创建一个函数,它接受一个 IQueryable 和一个 DateTime 属性 在那个 IQueryable 中,returns 一个 IQueryable 与另一个日期时间进行比较。

目前,我必须为几个不同的函数执行此操作,并且此代码可能会更改,因此我希望它能够普遍应用于 IQueryable:

  FMGQueryableSet = FMGQueryableSet.Where(t => t.Created.Day >= StartDate.Value.Day)
                               .Where(t => t.Created.Month >= StartDate.Value.Month)
                               .Where(t => t.Created.Year >= StartDate.Value.Year);

相反,我希望能够这样做:

FMGQueryableSet = SetDateCompare(FMGQueryableSet, t=> t.Created, StartDate, false)

方法存根看起来像这样,但是我不知道如何将传入的 属性 与 IQueryable

联系起来
public IQueryable<T> SetDateCompare<T>(IQueryable<T> OriginalQuery, Expression<Func<DateTime>> QueryProperty, DateTime ComparisonDate, bool isGreaterThan = true) 
    where T : class
    {
        if(isGreaterThan)
        {
            OriginalQuery = OriginalQuery.Where(QueryProperty >= ComparisonDate.Day)
                            .Where(QueryProperty >= ComparisonDate.Month)
                            .Where(QueryProperty >= ComparisonDate.Year);
        }
        else
        {
            OriginalQuery = OriginalQuery.Where(QueryProperty <= ComparisonDate.Day)
                           .Where(QueryProperty <= ComparisonDate.Month)
                           .Where(QueryProperty <= ComparisonDate.Year);

        }
        return OriginalQuery;

    }

你想要调用的 t=> t.Created 部分是一个 lambda 表达式,代表一个从类型 T 获取 DateTime 的函数,所以你的参数应该是 Func< T,DateTime> .

考虑到这一点,您应该试试这个:

public IQueryable<T> SetDateCompare<T>(IQueryable<T> OriginalQuery, Func<T,DateTime> getDateFunc, DateTime ComparisonDate, bool isGreaterThan = true)
where T : class
{
    if (isGreaterThan)
    {
        OriginalQuery = OriginalQuery.Where(t => getDateFunc(t).Day >= ComparisonDate.Day)
                        .Where(t => getDateFunc(t).Month >= ComparisonDate.Month)
                        .Where(t => getDateFunc(t).Year >= ComparisonDate.Year);
    }
    else
    {
        OriginalQuery = OriginalQuery.Where(t => getDateFunc(t).Day <= ComparisonDate.Day)
                        .Where(t => getDateFunc(t).Month  <= ComparisonDate.Month)
                        .Where(t => getDateFunc(t).Year  <= ComparisonDate.Year);

    }
    return OriginalQuery;

}

假设您真的想要您展示的不寻常的比较逻辑,下面是您的操作方法。如果您需要不同的比较逻辑,只需更改表达式 ymdCompareLess 和 ymdCompareGreater。

首先要做的是创建一个可以传递给 where 子句的新表达式。您希望能够传入一个表达式,指示要比较的 属性 和要在比较中使用的日期值。

我在下面创建了 2 个表达式用于小于比较和大于比较。根据为 isGreaterThan 传入的值,我们 select 大于比较或小于比较的表达式。

我正在使用我第一次在 Whosebug 上看到的 Replacement Visitor 模式,通过用其他表达式替换所有原始参数来创建一个新表达式。

一旦我们select编辑了要使用的表达式,我们就替换该表达式的参数。第一个参数 v1 将替换为我们的 QueryProperty 主体。假设您传递了类似 v => v.CreatedDate 的内容,该表达式的主体将是 CreatedDate.

第二个参数 v2 将替换为包含您传入的日期值的表达式常量。

结果将是创建的表达式类似于 v => v.CreatedDate.Day >= (new DateTime(2005, 2, 3)).Day,其中 v 是您传入的表达式的参数。

然后我们创建一个lambda表达式,以创建的表达式为主体,你传入的参数为参数。

最后,我们使用新的 lambda 表达式作为过滤器调用 .Where 方法,return 结果。

Expression<Func<DateTime, DateTime, bool>> ymdCompareLess = (v1, v2) => v1.Day <= v2.Day && v1.Month <= v2.Month && v1.Year <= v2.Year;
Expression<Func<DateTime, DateTime, bool>> ymdCompareGreater = (v1, v2) => v1.Day >= v2.Day && v1.Month >= v2.Month && v1.Year >= v2.Year;

public IQueryable<T> SetDateCompare<T>(IQueryable<T> OriginalQuery, Expression<Func<T, DateTime>> QueryProperty, DateTime ComparisonDate, bool isGreaterThan = true)
        where T : class
{
    LambdaExpression comparisonExpression = isGreaterThan ? ymdCompareGreater : ymdCompareLess;

    var replaceVisitor = new ReplaceVisitor(
        comparisonExpression.Parameters.ToArray(),
        new[] { QueryProperty.Body, Expression.Constant(ComparisonDate) }
        );

    var whereBody = replaceVisitor.Visit(comparisonExpression.Body);

    var whereClause = Expression.Lambda<Func<T, bool>>(whereBody, QueryProperty.Parameters);

    return OriginalQuery.Where(whereClause);
}

private class ReplaceVisitor : ExpressionVisitor
{
    Expression[] _from;
    Expression[] _to;
    public ReplaceVisitor(Expression[] from, Expression[] to)
    {
        this._from = from;
        this._to = to;
    }

    public override Expression Visit(Expression node)
    {
        var idx = Array.IndexOf(_from, node);

        if (idx > -1)
        {
            return _to[idx];
        }
        return base.Visit(node);
    }
}