Filtering collections of data is a common task in software development. Often, you need to filter a collection of objects based on specific criteria or conditions. In this blog post, we’ll explore how to filter a collection of objects using dynamic filtering criteria with expressions in C#. We’ll build a flexible filtering mechanism that allows us to filter a list of Person
objects based on different conditions such as “StartsWith” for names and numeric comparisons for ages.
Prerequisites
Before we dive into the code, make sure you have:
- A code editor such as Visual Studio or Visual Studio Code.
- Basic knowledge of C# and .NET.
The Scenario
Suppose we have a list of Person
objects, and we want to filter this list based on various conditions. We don’t want to write separate filtering logic for each condition; instead, we want a flexible and reusable approach.
The Code
The Person
Class
We start with a simple Person
class that has Name
and Age
properties:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
The Filtering Criteria
We define a FilterCriteria
class to hold our filtering conditions. Each condition consists of a PropertyName
, an Operator
, and a Value
. For example, we may want to filter by Name
starting with a certain character or Age
greater than or equal to a specific value:
public class FilterCriteria<T>
{
public List<FilterCondition<T>> Conditions { get; set; }
}
public class FilterCondition<T>
{
public string PropertyName { get; set; }
public string Operator { get; set; }
public object Value { get; set; }
}
Dynamic Filtering with Expressions
Now, the heart of our filtering mechanism lies in the FilterCollection
method. This method takes an IQueryable
collection, a set of filtering criteria, and returns a filtered collection:
public static IQueryable<T> FilterCollection<T>(IQueryable<T> collection, FilterCriteria<T> criteria)
{
var parameter = Expression.Parameter(typeof(T), "item");
Expression expression = Expression.Constant(true);
foreach (var condition in criteria.Conditions)
{
Expression binaryExpression;
if (condition.Operator == "StartsWith")
{
// Create an expression for the StartsWith condition
var property = Expression.Property(parameter, condition.PropertyName);
var value = Expression.Constant(condition.Value);
binaryExpression = Expression.Call(property, typeof(string).GetMethod("StartsWith", new[] { typeof(string) }), value);
}
else
{
// Handle other operators
var property = Expression.Property(parameter, condition.PropertyName);
var value = Expression.Constant(Convert.ChangeType(condition.Value, property.Type));
binaryExpression = Expression.MakeBinary(GetExpressionType(condition.Operator), property, value);
}
expression = Expression.AndAlso(expression, binaryExpression);
}
var lambda = Expression.Lambda<Func<T, bool>>(expression, parameter);
return collection.Where(lambda);
}
In this method:
- We create an
Expression
tree that represents the filtering logic. - We loop through each filtering condition and dynamically create the appropriate expression based on the condition’s operator.
- For conditions with the “StartsWith” operator, we call the
StartsWith
method on the property. - For other operators (e.g., “Equal,” “GreaterThan”), we create binary expressions using the
MakeBinary
method.
Running the Filtering
Finally, in the Main
method, we demonstrate how to use our dynamic filtering mechanism:
public static void Main()
{
var people = new List<Person>
{
new Person { Name = "Alice", Age = 30 },
new Person { Name = "Bob", Age = 25 },
new Person { Name = "Charlie", Age = 40 },
new Person { Name = "David", Age = 22 }
};
var filterCriteria = new FilterCriteria<Person>
{
Conditions = new List<FilterCondition<Person>>
{
new FilterCondition<Person> { PropertyName = "Name", Operator = "StartsWith", Value = "A" },
new FilterCondition<Person> { PropertyName = "Age", Operator = "GreaterThanOrEqual", Value = 30 }
}
};
var filteredPeople = FilterCollection(people.AsQueryable(), filterCriteria);
foreach (var person in filteredPeople)
{
Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");
}
}
In this example, we filter the people
collection based on two conditions: names starting with “A” and ages greater than or equal to 30.
Conclusion
Dynamic filtering of collections using expressions in C# provides a powerful and flexible way to filter data based on various conditions. With the FilterCollection
method, you can easily filter collections of objects with different filtering criteria without writing separate filtering logic for each condition. This approach improves code reusability and maintainability, making your code more robust and efficient.
Feel free to extend this mechanism to handle additional operators or complex filtering scenarios based on your specific requirements. Happy coding!