HiveBrain v1.2.0
Get Started
← Back to all entries
snippetcsharpMinor

Operator-based switch for a database query filter

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
operatorquerydatabasefilterforbasedswitch

Problem

I am currently working on a method, that can search based on a value and an operator.

Currently I check which operator was supplied to the the method in a switch, and do operations accordingly to it.

This is the current code:

switch (enumOperator)
{
    case Operator.Equal:
        tmpResult = tmpResult.Where(a => a.NUMERIC_VALUE == value);
        break;
    case Operator.Less:
        tmpResult = tmpResult.Where(a => a.NUMERIC_VALUE  a.NUMERIC_VALUE  a.NUMERIC_VALUE > value);
        break;
    case Operator.MoreOrEqual:
        tmpResult = tmpResult.Where(a => a.NUMERIC_VALUE >= value);
        break;
    case Operator.NotEqual:
        tmpResult = tmpResult.Where(a => a.NUMERIC_VALUE != value);
        break;
}


Is there a better/more succinct way to represent the previous switch in c#?

Additional info

The previous piece of code is used to search in a database based on the parameter(the field to be searched by), the value and operator.

So the previous code is copied for each field (a => a.X, a => a.Y ...).

As some of the fields are of different type, the "ugly" code is a bit changed and copied again.

Besides of the big switch (which by @unholysampler answer) will be nicer, the code a => a.X == value will be repeated many times(3).

Would it be also possible to save predicates where a => a.X == value, X is yet to be specified?

Solution

There are a few ways to approach this base on how much you want to minimize the code.

Define the predicates as actual functions and pass them in by name.

case Operator.Equal:
    tmpResult = tmpResult.Where(NumericEqual);
    break;


Use a temporary variable for the argument to Where()

Func predicate;
switch (enumOperator)
{
    case Operator.Equal:
        predicate = a => a.NUMERIC_VALUE == value;
        break;
    case Operator.Less:
        predicate = a => a.NUMERIC_VALUE < value;
        break;
    // ...
}
tmpResult = tmpResult.Where(predicate);


This could also be done with a function that encapsulates the switch and hiding it from the main function.

Or, if you feel like the switch statement is causing too much boilerplate, you could make a Dictionary> and insert a function pointer for each operator. Then the code that uses it becomes:

tmpResult = tmpResult.Where(operatorMap[enumOperator]);


It is a trade off. The shorter code requires the use of more memory. Handling the case where enumOperator does not have match (which is not currently being done) would also have to change.

For the case where you will have lots of different properties that all need similar operations, you can have a function that creates the predicate.

Func MakePredicateEqual(Func getField, X value)
{
    return a => getField(a) == value;
}


However, as things become more generic, more things need to be passed in as arguments and plumbing things together gets more complicated. It needs to be determined when the diminishing returns make it not worth it.

Code Snippets

case Operator.Equal:
    tmpResult = tmpResult.Where(NumericEqual);
    break;
Func<T, Boolean> predicate;
switch (enumOperator)
{
    case Operator.Equal:
        predicate = a => a.NUMERIC_VALUE == value;
        break;
    case Operator.Less:
        predicate = a => a.NUMERIC_VALUE < value;
        break;
    // ...
}
tmpResult = tmpResult.Where(predicate);
tmpResult = tmpResult.Where(operatorMap[enumOperator]);
Func<T, Boolean> MakePredicateEqual(Func<T, X> getField, X value)
{
    return a => getField(a) == value;
}

Context

StackExchange Code Review Q#124268, answer score: 6

Revisions (0)

No revisions yet.