Reputation: 26940
I'm trying to make an extension method for restrictions on a DateTime? property. It's for a search query and I really don't want to duplicate this code for all the possible date fields.
public static IQueryOver<T, T> WhereInOpenEndedDateRange<T>(this IQueryOver<T, T> query,
Expression<Func<object>> field,
DateTime? rangeFrom,
DateTime? rangeTo)
{
if(rangeFrom.HasValue && rangeTo.HasValue)
{
query.WhereRestrictionOn(field).IsBetween(rangeFrom.Value).And(rangeTo.Value);
}
else if (rangeFrom.HasValue)
{
//query.Where(() => /* help */ >= rangeFrom.Value);
}
else if (rangeTo.HasValue)
{
//query.Where(() => /* help */ <= rangeTo.Value);
}
return query;
}
I think I'm missing a fundamental aspect of expressions. Is it possible to pass in some form of Expression parameter and use it in the /* help */
spots?
Thanks
update
getting closer, but still feel so very far away...
else if (rangeFrom.HasValue)
{
var lt = Expression.LessThanOrEqual(field, Expression.Constant(rangeFrom, typeof(DateTime?)));
var b = Expression.Lambda<Func<bool>>(lt);
query.Where(b);
}
but this will not work as it'd be comparing Func<object>
to DateTime?
. How can I merge the original property expression into a new Func<bool>
and preserve the needed bits to keep the NH QueryOver in tact?
After looking at NH source for QueryOverRestrictionBuilder I will do
string propertyName = ExpressionProcessor.FindMemberExpression(field.Body)
and build the Restrictions with Criteria methods.
Upvotes: 3
Views: 1113
Reputation: 4283
Well I couldn't figure out how to do this using an
Expression<Func<bool>>
signature, but this is only for DateTime?s anyway, so I don't see how the restriction I added will make any difference.
Basically, you needed expression (field in your example) to exactly match the parameter you were sending in, i.e. x=>x.SomeNullableDateField. 'x' is the type of query you started out with
session.QueryOver<YourClass>()
so it needed to be included in the type of expression being sent in. You also missed getting hold of that parameter, using
ParameterExpression param = expression.Parameters.Single()
to use for constructing the comparison expressions.
You were on to converting the BinaryExpression to Expression<Func<bool>>
, but you had to take it one step further to Expression<Func<T, bool>>
.
public static IQueryOver<T, T> WhereInOpenEndedDateRange<T>(this IQueryOver<T, T> query,
Expression<Func<T, DateTime?>> expression,
DateTime? rangeFrom,
DateTime? rangeTo) where T : class
{
// Lambda being sent in
ParameterExpression param = expression.Parameters.Single();
if(rangeFrom.HasValue && rangeTo.HasValue)
{
// GT Comparison
var expressionGT =
Expression.GreaterThanOrEqual(
expression.Body,
Expression.Constant(rangeFrom.Value, typeof(DateTime?)
)
);
// LT Comparison
var expressionLT =
Expression.LessThanOrEqual(
expression.Body,
Expression.Constant(rangeTo.Value, typeof(DateTime?)
)
);
query.Where(
Expression.Lambda<Func<T, bool>>(expressionGT, param))
.And(
Expression.Lambda<Func<T, bool>>(expressionLT, param)
);
}
else if(rangeFrom.HasValue)
{
// GT Comparison
BinaryExpression expressionGT =
Expression.GreaterThanOrEqual(
expression.Body,
Expression.Constant(rangeFrom.Value, typeof(DateTime?)
)
);
// covert to lambda
query.Where(Expression.Lambda<Func<T, bool>>(expressionGT, param));
}
else if(rangeTo.HasValue)
{
// LT Comparison
BinaryExpression expressionLT =
Expression.LessThanOrEqual(
expression.Body,
Expression.Constant(rangeTo.Value, typeof(DateTime?)
)
);
query.Where(Expression.Lambda<Func<T, bool>>(expressionLT, param));
}
return query;
}
Usage
var test = session.QueryOver<MyPocoClass>()
.WhereInOpenEndedDateRange(x=>x.SomeNullableDateField, DateTime.Now, null);
Upvotes: 2