Parsing Date portion of a DateTime with a BinaryExpression

1.5k views Asked by At

I've found answers for a similar concept, but none of them seems to fit my case.

I'm building an object that filters results by a Where clause and a Lambda for types known at runtime.

A filter array can contains one or more filters with a property name, and equality operator (i.e. gte, eq, neq, etc) and the value to compare.

Since I could have more than one property to filter, I first grab all the BinaryExpression that I need to compare any object property and merge them in a single expression that gets passed to a query Where clause.

If the filter's property type is a DateTime I want to compare only the Date portion of the property using a BinaryExpression.

I've a method that receives the filter's parameter name and its value.

public static Expression<Func<T, bool>> GetEqualExpression<T>(string parameterName,
            string comparisonValue) {

    var param = Expression.Parameter(typeof(T), parameterName);
    var property = Expression.Property(param, parameterName);

    var propInfo = (PropertyInfo) property.Member;
    var propType = propInfo.PropertyType;

    if (propType == typeof(DateTime)) {

        // Parse the string to a DateTime.
        const string dateFormat = "ddd MMM d yyyy HH:mm:ss 'GMT'K";
        var parsedDate = DateTime.ParseExact(comparisonValue, dateFormat, CultureInfo.InvariantCulture); 

        // Trying to access the 'Date' child property.
        property = Expression.Property(property, "Date");
        var constant = Expression.Constant(parsedDate.Date);


        return Expression.Lambda<Func<T, bool>>(Expression.Equal(property, constant), param);
    }

    ...
}

considering for example an object as:

public class Ticket {
    public DateTime StartDate {get; set;}
    public DateTime DueDate {get; set;}
}

and a filter for the 'StartDate' property, when I pass the expression to the query's where clause, it fails with exception "Lambda parameter not in scope.";

What I'm missing?

Thank you in advance.

Update

So, thanks to xanatos I've discovered that ServiceStack.OrmLite (the ORM I'm using) doesn't support accessing the Date property, much like EF. So now the question is, How can I solve this situation?

2

There are 2 answers

0
Roberto Mauro On BEST ANSWER

So, this is how I managed the situation. I'd like to really thanks xanatos to point me out to the DateTime.Date issue with the ORM.

I've decided to compare dates equality by time range in the following manner:

public static Expression<Func<T, bool>> GetEqualExpression(string parameterName, 
    string comparisonValue)
{
    var param = Expression.Parameter(typeof (T), parameterName);
    var prop = Expression.Property(param, parameterName);

    var propInfo = (PropertyInfo) Property.Member;
    var propType = propInfo.PropertyType;

    if (propType == typeof(DateTime))
    {
        const string dateFormat = "ddd MMM d yyyy HH:mm:ss 'GMT'K";
        var parsedDate = DateTime.ParseExact(comparisonValue, dateFormat, CultureInfo.InvariantCulture);

        var dayStart = new DateTime(parsedDate.Year, parsedDate.Month, parsedDate.Day, 0, 0, 0, 0);
        var dayEnd = new DateTime(parsedDate.Year, parsedDate.Month, parsedDate.Day, 23, 59, 59, 999);

        var left = Expression.Lambda<Func<T, bool>>(Expression.GreaterThanOrEqual(prop, Expression.Constant(dayStart)), param);
        var right = Expression.Lambda<Func<T, bool>>(Expression.LessThanOrEqual(prop, Expression.Constant(dayEnd)), param);

        return left.Compose(right, Expression.And);
    }

    ...
}

This seems to do the trick.

4
xanatos On

There is a small error: you must use

Expression.Equal(property, constant)

instead of

Expression.Equals(property, constant)

And then perhaps you have a problem in the Since I could have more than one property to filter, I first grab all the BinaryExpression that I need to compare any object property and merge them in a single expression that gets passed to a query Where clause part

Note that if you are using Entity Framework, the DateTime.Date is probably not supported. See https://stackoverflow.com/a/21186498/613130 . One possible solution is to use EntityFunctions.TruncateTime.