Modifying the Filter Pattern with Generic Lambda Expressions

3 min


We can optimize the Pipeline & Filter pattern (pipeline and filters) by reducing the amount of code required to implement it by using a lambda expression (simplified notation of anonymous methods) as a specific filter condition. As an example to demonstrate this concept, a WPF application with a user interface was chosen. Here is its source code.

Pipeline & Filter pattern

Typically, within this pattern, an interface is implemented for each new filter. Instead of implementing an interface for each filter condition, you can use a generic lambda expression as input to the filter pipeline. This will result in less code. Below is a class diagram:

ConditionAggregator is the pipelined class that holds the collection Condition<T>Filter<T> owns ConditionAggregator or Condition<T> to apply filtering conditions to the dataset. When the apply function is called Filter<T>, the check method is executed ICondition<T>ConditionAggregator<T> has an event OnFilterChanged… It fires when the value of a collection or condition changes in the classes of the view model. The next section will describe the use of the Filter pattern by the view model.

The code

Use in the view model

An explanation of the MVVM pattern can be found at this link… One of the responsibilities of the View Model in MVVM is to handle user interaction and data changes. In our case, changes in the values ​​of the filtering conditions must be passed to the business layer in order to apply filters to a specific collection of data. Changing the value of a condition in the view model streams the event ConditionAggregator<T> OnFilterChangedsubscribed to by the apply filter method. Below is a class diagram of the view model.

Entity class Employee created to store information about employees. Generic a type T design pattern Filter will be replaced by class EmployeeEmployeeList contains a list of employee data and filters to apply. The constructor of the class gets the list of conditions and navigates to the list of filters.

public EmployeeList(IEmployeesRepository repository, ConditionAggregator<employee> conditionAggregator)
        {
            this.repository = repository;
            this.filters = new ConcreteFilter<employee>(conditionAggregator);
            conditionAggregator.OnFilterChanged += this.FilterList;
            _ = initAsync();
        }

Method FilterList subscribed to the event OnFilterChanged to apply filters to data when a condition or value changes.

private void FilterList()
{
    this.Employees = this.filters.Apply(this.employeesFullList);
}

EmployeesViewModel connected to the user interface. This example shows only one property filter EmployeeTypeselectedbut in ConditionAggregator many filters can be passed. The next piece of code is the constructor method that registers the filter condition.

public EmployeesViewModel(IEmployeesRepository repository)
        {
            this.repository = repository;
            Condition&lt;employee&gt; filterEmployee = new Condition&lt;employee&gt;((e) =&gt; e.employeeCode == this.EmployeeTypeSelected);
            this.conditionAggregator = new ConditionAggregator&lt;employee&gt;(new List&lt;condition&lt;employee&gt;&gt; { filterEmployee });
            this.EmployeeList = new EmployeeList(repository, this.conditionAggregator);            
        }  
&lt;/condition&lt;employee&gt;&lt;/employee&gt;&lt;/employee&gt;&lt;/employee&gt;

The condition is passed as a lambda expression. There can be any number of them, since the constructor ConditionAggregator takes a list of filtering conditions. The main goal of reducing the code required to create a specific class of filter conditions has been achieved.

Application architecture

WPF.Demo.DataFilter is a WPF UI view. It has one grid and one combo box for filtering. Project WPF.Demo.DataFilter.ViewModels processes data, filters changes, and reloads data to update the user interface. Project WPF.Demo.DataFilter.Common is a complete implementation of the Pipeline & Filter pattern. WPF.Demo.DataFilter.DAL loads a simple json file as data store.

This is the main interface:


The translation of the article was prepared in anticipation of the start of the course “Architecture and Design Patterns”… And right now we invite everyone to free demo lesson within the framework of which we will discuss the purpose and structure of the “Interpreter” pattern, the Backus-Nauer forms, lexical, syntactic and semantic analyzes, as well as analyze practical examples. You can sign up for a lesson by link



0 Comments

Leave a Reply