The NumericTypesExpressionCreator class provides a robust solution for creating expressions tailored to numeric comparisons. In this blog post, we will take an in-depth look at the structure, methods, and underlying logic of this class, shedding light on how it empowers developers to handle dynamic numeric filtering scenarios.

internal sealed class NumericTypesExpressionCreator : IExpressionCreator
{
    public List<Expression<Func<T, bool>>> CreateExpressions<T>
        (string searchValue, Expression<Func<T, object>> keySelector)
    {
        bool numericParsingResult =
            searchValue.All(char.IsDigit);
        if (!numericParsingResult)
        {
            return new List<Expression<Func<T, bool>>>();
        }

        List<MemberExpression> numericMemberExpressions = 
            keySelector.Body.ExtractNumericMemberExpressions();

        var numericExpressions = new List<Expression<Func<T, bool>>>();
        foreach (var numericMemberExpression in numericMemberExpressions)
        {
            List<string> properties = numericMemberExpression.GetPropertyChain();

            ParameterExpression parameterExpression = keySelector.Parameters.Single();

            Expression expression = parameterExpression;
            foreach (var property in properties)
            {
                expression = Expression.Property(expression, property);
            }

            bool conversionResult =
                numericMemberExpression.Type.TryChangeType(
                    searchValue, out dynamic numericSearchValue);

            if (!conversionResult)
            {
                continue;
            }

            MethodInfo equalsMethodInfo = numericMemberExpression.Type.GetMethod
                ("Equals", new[] { numericMemberExpression.Type })!;

            ConstantExpression constantExpression = Expression.Constant
                (numericSearchValue, numericMemberExpression.Type);

            MethodCallExpression methodCallExpression = Expression.Call
                (expression, equalsMethodInfo, constantExpression);

            Expression<Func<T, bool>> numericExpression = Expression.Lambda<Func<T, bool>>
                (methodCallExpression, parameterExpression);

            numericExpressions.Add(numericExpression);
        }

        return numericExpressions;
    }
}

The stages of the CreateExpressions method are as follows:

  1. Numeric Parsing Check: The method begins by checking whether the provided search value consists entirely of digits using the All LINQ method. If the check fails, indicating a non-numeric search value, an empty list of expressions is returned.
  2. Extracting Numeric Member Expressions: The class proceeds to extract all numeric member expressions from the provided keySelector using the ExtractNumericMemberExpressions extension method.
  3. Iterating Over Numeric Member Expressions: For each numeric member expression, the method retrieves the associated property chain.
  4. Building Property Access Chain: It constructs the property access chain within the expression, preparing it for further manipulation.
  5. Type Conversion: The method attempts to convert the provided search value to the type of the numeric member expression using the TryChangeType method.
  6. Dynamic Method Invocation: It dynamically invokes the Equals method for the specific numeric type.
  7. Creating Expression: The final step involves creating a Lambda expression encapsulating the method call expression, resulting in an Expression<Func<T, bool>>.
  8. Aggregating Expressions: All generated expressions are added to the list, which is then returned as the method output.