简体   繁体   中英

NH QueryOver extensions: How to merge member expression into another expression?

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.

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, ie 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);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM