通用 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);
}
}
所以我正在尝试创建一个函数,它接受一个 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);
}
}