3
votes

I have a predicate handler that tests almost 200 cases, and each test involves five possible comparisons. I want to streamline this code, but am hitting a wall with how to express this syntactically.

public static Expression<Func<OADataConsolidated, bool>> Match(DOTSearchFilter filters)
{
    var predicate = (filters.OrFilters.Count > 0) ? PredicateBuilder.False<OADataConsolidated>() : PredicateBuilder.True<OADataConsolidated>();

    foreach (DOTFilter f in filters.AndFilters)
    {
        int value = -1;
        int.TryParse(f.TextValue, out value);
        switch (f.Type)
        {
            case DOTFilter.FilterType.SCO:
                switch (f.IdValue)
                {
                    case 4: // GED: Reasoning
                        switch (f.Comp)
                        {
                            case DOTFilter.Comparitor.LessThan:
                                predicate = predicate.And(p => p.ajblGEDR_Mean < value);
                                break;
                            case DOTFilter.Comparitor.EqualOrLess:
                                predicate = predicate.And(p => p.ajblGEDR_Mean <= value);
                                break;
                            case DOTFilter.Comparitor.EqualTo:
                                predicate = predicate.And(p => p.ajblGEDR_Mean == value);
                                break;
                            case DOTFilter.Comparitor.EqualOrGreater:
                                predicate = predicate.And(p => p.ajblGEDR_Mean >= value);
                                break;
                            case DOTFilter.Comparitor.GreaterThan:
                                predicate = predicate.And(p => p.ajblGEDR_Mean > value);
                                break;
                        }
                        break;
                    case 5: // GED: Mathematics
                        switch (f.Comp)
                        {
                            case DOTFilter.Comparitor.LessThan:
                                predicate = predicate.And(p => p.ajblGEDM < value);
                                break;
                            case DOTFilter.Comparitor.EqualOrLess:
                                predicate = predicate.And(p => p.ajblGEDM <= value);
                                break;
                            case DOTFilter.Comparitor.EqualTo:
                                predicate = predicate.And(p => p.ajblGEDM == value);
                                break;
                            case DOTFilter.Comparitor.EqualOrGreater:
                                predicate = predicate.And(p => p.ajblGEDM >= value);
                                break;
                            case DOTFilter.Comparitor.GreaterThan:
                                predicate = predicate.And(p => p.ajblGEDM > value);
                                break;
                        }
                        break;

The above switch statement is repeated almost 200 times and the only thing different in each case is the field name being checked. I want to reduce this code as much as possible.

1
I hope you're not using -1 as a sentinel for value -- TryParse is guaranteed to set the value no matter what; if it returns false, it will have set value to 0. In other words, value will be equal to -1 only if f.TextValue parses to -1. And why aren't you checking the return value of TryParse?phoog

1 Answers

2
votes

You can build the expression dynamically like that:

string propertyName = GetPropertyName(f);
ExpressionType comp = GetComparisonType(f);
ParameterExpression p = Expression.Parameter(typeof(OADataConsolidated));
Expression<Func<OADataConsolidated, bool>> expr =
    Expression.Lambda<Func<OADataConsolidated, bool>>(
        Expression.MakeBinary(
            comp,
            Expression.Property(p, propertyName),
            Expression.Constant((double)value)),
        p);

predicate = predicate.And(expr);


...

static string GetPropertyName(DOTFilter filter)
{
    switch(filter.IdValue)
    {
        case 4: // GED: Reasoning
            propertyName = "ajblGEDR_Mean";
            break;
        case 5: // GED: Mathematics
            propertyName = "ajblGEDM";
            break;
        ...
        default:
            throw new ArgumentException("Unknown Id value");
    }
}

static ExpressionType GetComparisonType(DOTFilter filter)
{
    switch (filter.Comp)
    {
        case DOTFilter.Comparitor.LessThan:
            return ExpressionType.LessThan;
        case DOTFilter.Comparitor.EqualOrLess:
            return ExpressionType.LessThanOrEqual;
        case DOTFilter.Comparitor.EqualTo:
            return ExpressionType.Equal;
        case DOTFilter.Comparitor.EqualOrGreater:
            return ExpressionType.GreaterThanOrEqual;
        case DOTFilter.Comparitor.GreaterThan:
            return ExpressionType.GreaterThan;
        default:
            throw new ArgumentException("Unknown Comp value");
    }
}

The switches are still there, but they're not repeated.