Building LINQ to Entities Expression tree with Dat

2020-07-25 01:40发布

问题:

I am trying to complete implementation of dynamic querying provided by Todd Sprang, please see this link. Currently I am trying to enable filtering by DateTime type, and I am struggling to make lambda expression using "greater than" and "less than" operators, so my SQL would appear like "WHERE someDate<='2017-04-27 00:00:00' AND someDate<='2017-04-27 23:59:59'.

I managed to extract an operator methods:

private static readonly MethodInfo DateTimeGreaterThanOrEqualMethod = typeof(DateTime).GetMethod("op_GreaterThanOrEqual",
                                BindingFlags.Static | BindingFlags.Public);
private static readonly MethodInfo DateTimeLessThanOrEqualMethod = typeof(DateTime).GetMethod("op_LessThanOrEqual",
                        BindingFlags.Static | BindingFlags.Public);

And here is my DateTime lambda filter method:

    private static Expression<Func<TDbType, bool>> ApplyDateTimeCriterion<TDbType,
            TSearchCriteria>(TSearchCriteria searchCriteria, PropertyInfo searchCriterionPropertyInfo,
            Type dbType, MemberInfo dbFieldMemberInfo, Expression<Func<TDbType, bool>> predicate)
{
    var searchDateTime = searchCriterionPropertyInfo.GetValue(searchCriteria) as DateTime?;
    if (searchDateTime == null)
    {
        return predicate;
    }
    var valueDateMin = ((DateTime)searchDateTime).Date;
    var valueDateMax = new DateTime(valueDateMin.Year, valueDateMin.Month, valueDateMin.Day, 23, 59, 59);

    var dbTypeParameter = Expression.Parameter(dbType, @"x");
    var dbFieldMember = Expression.MakeMemberAccess(dbTypeParameter, dbFieldMemberInfo);
    var criterionConstantMin = new Expression[] { Expression.Constant(valueDateMin) };
    var criterionConstantMax = new Expression[] { Expression.Constant(valueDateMax) };
    //having problem down here, when trying to call static method which needs two parameters
    var greaterThanCall = Expression.Call(dbFieldMember, DateTimeGreaterThanOrEqualMethod, criterionConstantMin);
    var lambdaGreaterThan = Expression.Lambda(greaterThanCall, dbTypeParameter) as Expression<Func<TDbType, bool>>;

    var lessThanCall = Expression.Call(dbFieldMember, DateTimeLessThanOrEqualMethod, criterionConstantMax);
    var lambdaLessThan = Expression.Lambda(greaterThanCall, dbTypeParameter) as Expression<Func<TDbType, bool>>;

    return predicate.And(lambdaGreaterThan).And(lambdaLessThan);
}

I am having problem on calling Expression.Call because op_GreaterThanOrEqual is a static method with 2 parameters. Maybe I should not call op_GreaterThanOrEqual, maybe this has to be done in another way?

回答1:

I am having problem on calling Expression.Call because op_GreaterThanOrEqual is a static method with 2 parameters

Expression.Call has several overloads, you need to use the proper overload for static methods (currently you are using the one with instance argument, hence for instance method).

Also don't create unnecessary Expression[] (the methods that receive multiple expressions usually use params Expression[], so it will be created for you when needed):

var criterionConstantMin = Expression.Constant(valueDateMin);
var criterionConstantMax = Expression.Constant(valueDateMax);

And here are the calls in question

var greaterThanCall = Expression.Call(DateTimeGreaterThanOrEqualMethod, dbFieldMember, criterionConstantMin);
var lessThanCall = Expression.Call(DateTimeLessThanOrEqualMethod, dbFieldMember, criterionConstantMax);

maybe this has to be done in another way?

There are specific Expression method like GreaterThan etc. corresponding to each C# comparison operator, so you don't bother getting operator method infos etc. and simply use:

var greaterThanCall = Expression.GreaterThanOrEqual(dbFieldMember, criterionConstantMin);
var lessThanCall = Expression.LessThanOrEqual(dbFieldMember, criterionConstantMax);

Update: In order to support DateTime? members, use the following:

var criterionConstantMin = Expression.Constant(valueDateMin, dbFieldMember.Type);
var criterionConstantMax = Expression.Constant(valueDateMax, dbFieldMember.Type);

and then

var greaterThanCall = Expression.GreaterThanOrEqual(dbFieldMember, criterionConstantMin);
var lessThanCall = Expression.LessThanOrEqual(dbFieldMember, criterionConstantMax);