Generic filter in C# by using builder pattern5 min read

Builder pattern with expression trees in a real world, example, is the main topic of today!

So what is builder pattern?

Well it’s the best practice used to build a complex object in a easy, step by step approach.

Using the Builder interface we control a steps or order in which we build the final object. As there are already great Builder pattern articles (just google it by yourself), I’ll try to give you some more real life example.

We will build a filter functionality using builder pattern.
To be more precise, we’ll build generic filter.

Why generic filter you ask?

Well, you do want to have the possibility to send from frontend as much filter values as you wish! It doesn’t make sense to write additional logic regarding each filter for itself, right!?

In order to create this kind of functionality you will need to have a good grasp about expression trees as well. If you don’t, stay tuned for my next article! Where I’ll be writing about it and hopefully make it clear to you why and when should you use it!?

But firstly, let’s make use of builder pattern!

Option 1

The final result of any builder pattern is to call BUILD() method which in return gives object of a class we are building.

Simplified example:

public class FilterBuilder
{
    private Filter _filter;

	/*
	 I skipped here some more methods in favor of simplifying things
	*/

    public Filter Build()
    {
        return _filter;
    }
}

I.E.
var filter = new FilterBuilder()./*some more method chaining*/.Build();

Option 2

Or the other option is to use static implicit operator.
Using it, we are ditching Build() method in favor of implicit conversion of an object.

Simplified example 2:

public class FilterBuilder
{
	/*
	 I skipped here some more methods in favor of simplifying things
	*/

    public static implicit operator Filter(FilterBuilder fb)
    {
        return new Fitler(fb); // again, this is pretty simplified example!
    } 
}

Come again!? What’s implicit operator anyways?

It’s a conversion operator which always succeeds and never throws an exception. Basically you can convert object form one type to the other.

Which means you can write something like this:

SomeClass _someClass = new SomeClass();
RandomClass _randomClass = _someClass;

For more info about implicit and explicit, please refer to: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/user-defined-conversion-operators

Ok, I moved a bit from my main topic.

Let’s go from scratch!

Let’s create imaginary Customer Controller with a method which returns filtered list of customers. I ditched some things inside this method just to provide you the clean example. This way you will get better picture of usage.

public async Task<Customers> GetList(string filterValues, string sortBy, OrderDirection orderDirection, int pageNumber, int pageSize, CUserPrincipal user)
{
	var result = new Customers();

	var filter = new FilterBuilder<Customer>()
		.AddFilters(filterValues)
		.CanViewAll(user)
		.Build();

	// not so relevant for our example
	var entityModels = await _uow.CustomerRepository.GetListAsync(
		filter, 
		pageNumber, 
		pageSize, 
		sortBy, 
		orderDirection, 
		x => x.Include(y => y.LastModifiedByUser));
	
	result.model = entityModels; // not relevant, so I simplified it here not to confuse you  

	
	return result;
}


As you can see we used FilterBuilder class to create filter based on bunch of string filterValues.

So how does this work and look like?

  1. we created result object based on Customers class
  2. we created filter expression tree based on generic FilterBuilder class
  3. we do the query using our unit of work and repository class
  4. we return results

Generic FilterBuilder class

public class FilterBuilder<T> where T : class
{
	private Expression<Func<T, bool>> _expression;

	public FilterBuilder<T> AddFilter(string filterBy, string filter)
	{
		if (filterBy == null || filter == null)
			return this;

		if (!string.IsNullOrEmpty(filterBy))
		{
			var p = typeof(T).GetProperty(filterBy,
				BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);

			Expression<Func<T, bool>> obj = x => p.GetValue(x, null) != null &&
												 p.GetValue(x, null).ToString().ToLower()
													 .Contains(filter.ToLower());

			_expression = obj.And(_expression);
		}

		return this;
	}

	public FilterBuilder<T> CanViewAll(CUserPrincipal user)
	{
		if (!user.CanViewAll)
			AddFilter("CreatedByUserId", user.UserId);
		return this;
	}

	 public FilterBuilder<T> AddFilters(string filterValues)
	{
		var filters = JsonConverter.TryParse<List<FilterValueItem>>(filterValues);
		if (filters != null)
		{
			foreach (var filter in filters.Where(x => !string.IsNullOrEmpty(x.FilterValue)))
			{
				AddFilter(filter.FilterKey, filter.FilterValue);
			}
		}

		return this;
	}


	public FilterBuilder<T> AddFilter(Expression<Func<T, bool>> obj)
	{
		_expression = obj.And(_expression);
		return this;
	}

	public FilterBuilder<T> AddOrFilter(Expression<Func<T, bool>> obj)
	{
		_expression = obj.Or(_expression);
		return this;
	}

	public Expression<Func<T, bool>> Build()
	{
		return _expression;
	}
}

The whole idea is to add a bunch of filters as array of strings. Then we create expression tree and chain each filter using AND or OR.

To chain these two we will need to create two extension methods for Expression tree.

AND – extension for Expression Tree

        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> a, Expression<Func<T, bool>> b)
        {
            switch (a)
            {
                case null when b == null:
                    return null;
                case null:
                    a = b;
                    break;
                default:
                {
                    if (b == null)
                    {
                        return Expression.Lambda<Func<T, bool>>(a.Body, a.Parameters[0]);
                    }

                    break;
                }
            }

            var p = a.Parameters[0];

            var visitor = new SubstExpressionVisitor {Subst = {[b.Parameters[0]] = p}};

            Expression body = Expression.AndAlso(a.Body, visitor.Visit(b.Body) ?? throw new InvalidOperationException());
            return Expression.Lambda<Func<T, bool>>(body, p);
        }

OR – extension for Expression Tree

        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> a, Expression<Func<T, bool>> b)
        {
            try
            {
                switch (a)
                {
                    case null when b == null:
                        return null;
                    case null:
                        a = b;
                        break;
                    default:
                    {
                        if (b == null)
                        {
                            b = a;
                        }

                        break;
                    }
                }

                var p = a.Parameters[0];

                var visitor = new SubstExpressionVisitor { Subst = { [b.Parameters[0]] = p } };

                Expression body = Expression.OrElse(a.Body, visitor.Visit(b.Body) ?? throw new InvalidOperationException());
                return Expression.Lambda<Func<T, bool>>(body, p);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
           
        }

As you can see there is another class called SubstExpressionVisitor which we used to create our Expression tree easier.

SubstExpressionVisitor class


    internal class SubstExpressionVisitor : ExpressionVisitor
    {
        public Dictionary<Expression, Expression> Subst = new Dictionary<Expression, Expression>();

        protected override Expression VisitParameter(ParameterExpression node)
        {
            return Subst.TryGetValue(node, out var newValue) ? newValue : node;
        }
    }

Conclusion

What we did above is, we just created expression tree based on our filters. Then we forwarded it to the repository class in which we use filter expression as an input inside .Where() linq method.

So no more tedious work creating per filter logic.

1 Comment Generic filter in C# by using builder pattern5 min read

Leave a Reply

Your email address will not be published. Required fields are marked *