0

I am trying to create a method where I'll pass a list of filters containing the field,comparer and value. Here is my parameter

public class QueryFilter
{
    public string Field { get; set; }
    public object Value { get; set; }   
    public string Comparer { get; set; }
}

Now, I created a method that will return an expression based on the list of QueryFilter that was passed.

public Expression<Func<TEntity, bool>> QueryFilterBuilder<TEntity>(List<QueryFilter> filters)
    {
        try
        {
            var predicate = PredicateBuilder.New<TEntity>(true);

            filters.ForEach(filter =>
            {
                switch (filter.Comparer.ToLower())
                {
                    case QueryFilterComparer.Eq: // equals
                        predicate = predicate.And(x => filter.Field == filter.value); // something like this
                        break;
                    case QueryFilterComparer.Ne: // not equal
                        // Add not equal filter logic here
                        break;
                    case QueryFilterComparer.Any: // any
                        break;
                    case QueryFilterComparer.Gt: // greater than
                        break;
                    case QueryFilterComparer.Gte: // greater than equal
                        break;
                    case QueryFilterComparer.Lt: // less than
                        break;
                    case QueryFilterComparer.Lte: // less than equal
                        break;
                    case QueryFilterComparer.Contains: // contains
                        break;
                    default:
                        break;
                };
            });

            return predicate;
        }
        catch (Exception)
        {
            throw;
        }
    }

The problem is I'm not sure how to implement this with generic class. Is it possible to do this?

Lenonskie
  • 193
  • 1
  • 2
  • 11

1 Answers1

1

You can implement this using Expression api like so:

public static Expression<Func<TEntity, bool>> QueryFilterBuilder<TEntity> 
(List<QueryFilter> filters)
{
    Expression GetExpressionForQueryFilter(QueryFilter filter, Expression param)
        => filter.Comparer switch
        {
            QueryFilterComparer.Eq => Expression.Equal(GetField(filter.Field, param), Expression.Constant(filter.Value)),
            QueryFilterComparer.Ne => Expression.Not(Expression.Equal(GetField(filter.Field, param), Expression.Constant(filter.Value))),
            QueryFilterComparer.Any => throw new NotImplementedException(),
            QueryFilterComparer.Gt => throw new NotImplementedException(),
            QueryFilterComparer.Gte => throw new NotImplementedException(),
            QueryFilterComparer.Lt => throw new NotImplementedException(),
            QueryFilterComparer.Lte => throw new NotImplementedException(),
            QueryFilterComparer.Contains => throw new NotImplementedException(),
            _ => throw new ArgumentOutOfRangeException()
    };

    Expression GetField(string field, Expression param)
    => Expression.Field(param, typeof(TEntity).GetField(field) ?? throw new ArgumentOutOfRangeException());

    var parameter = Expression.Parameter(typeof(TEntity), "parameter");

    return Expression.Lambda<Func<TEntity, bool>>(filters.Aggregate(
        (Expression) Expression.Constant(true),
        (acc, next) => Expression.MakeBinary(ExpressionType.AndAlso, acc, GetExpressionForQueryFilter(next, parameter))),
    parameter);
}
Clemens
  • 588
  • 6
  • 9
  • One issue: `ExpressionType.AndAlso` instead of `ExpressionType.And` – Svyatoslav Danyliv May 04 '22 at 09:10
  • @SvyatoslavDanyliv Thanks, I did not know this. As I read the docs this only omits evaluating the righthand expression when the lefthand expression is false, is this correct? – Clemens May 04 '22 at 14:23
  • `ExpressionType.And` is bitwise operator to work with bits. `ExpressionType.AndAlso` for boolean expressions. – Svyatoslav Danyliv May 04 '22 at 14:56
  • @SvyatoslavDanyliv That is true. And works fine for bool. Is there another difference than that only the lefthand expression is evaluated if false? – Clemens May 04 '22 at 17:14
  • Big difference in generated SQL. Check by yourself. – Svyatoslav Danyliv May 04 '22 at 17:18
  • @Clemens I'm having an exception - Exception: Value cannot be null. (Parameter 'field') on the GetField. Not sure why but I'm passing a string with field name of the entity – Lenonskie May 04 '22 at 23:37
  • I change code to this - var memberInfo = typeof(TEntity).GetField(field) and it returns null value but I'm passing a correct field name. I will look into this first – Lenonskie May 04 '22 at 23:47
  • @Lenonskie Are you sure that you are not trying to access a property? This would then be Expression.Property – Clemens May 05 '22 at 06:07