Рефакторинг метода фильтрации данных C#

Я создаю метод для получения данных и могу отфильтровать выборку по двум полям: Code и Value. Оба этих поля могут быть null (в таком случае мне не нужно фильтровать их вообще) и из-за этого я столкнулся с проблемой масштабирования подобного решения.

Ниже привожу свой код который работает как надо и верно выдаёт данные. Вопрос вот в чём: нормален ли тот факт, что при добавления новых фильтров по новым полям мне придётся расширять класс handler и вообще иметь кучу проверок? Как я могу от этого избавиться? какие best practices есть на этот счёт?

Класс обработчика запроса:

public async Task<IEnumerable<Product>> Handle(GetProductsQuery query, CancellationToken cancellationToken)
{
    var filters = new List<Expression<Func<Product, bool>>>();

    if (query.Code.HasValue) filters.Add(p => p.Code == query.Code);
    if (!string.IsNullOrEmpty(query.Value)) filters.Add(p => p.Value == query.Value);

    var products = await _productRepository.GetAll(filters);

    return products;
}

Класс репозитория, который обращается к базе:

public async Task<IEnumerable<Product>> GetAll(IEnumerable<Expression<Func<Product, bool>>> filters)
{
    IQueryable<Product> products = _context.Products;

    if (filters != null)
    {
        foreach (var filter in filters)
        {
            products = products.Where(filter);
        }
    }

    return await products.ToListAsync();
}

Ответы (1 шт):

Автор решения: aepot

Ну здесь в целом всё нормально. Единственное, что я бы оптимизировал, это выбросил бы машину состояний, так как await один и в самом конце метода.

public Task<IEnumerable<Product>> Handle(GetProductsQuery query, CancellationToken cancellationToken)
{
    var filters = new List<Expression<Func<Product, bool>>>();

    if (query.Code.HasValue)
        filters.Add(p => p.Code == query.Code);
    if (!string.IsNullOrEmpty(query.Value))
        filters.Add(p => p.Value == query.Value);

    return _productRepository.GetAll(filters);
}

public Task<IEnumerable<Product>> GetAll(IEnumerable<Expression<Func<Product, bool>>> filters)
{
    IQueryable<Product> products = _context.Products;

    foreach (var filter in filters)
    {
        products = products.Where(filter);
    }

    return products.ToListAsync();
}

Проверка filters на null тоже лишняя, исходя из логики вызывающего метода.


Избавиться от перечисления свойств не просто, можно попробовать через рефлексию.

var props = typeof(GetProductsQuery).GetProperties();

foreach (PropertyInfo pi in props)
{
    // здесь проверка на Value/Reference type
    // далее на 0/null и т.д. для всех поддерживаемых типов полей.
}

Но я не уверен, что так стоит делать и возможно для того чтобы EF это скушал, придется динамически строить Expression. В общем, копать куда-то туда, но советую заниматься этим не раньше, чем появится реальная потребность.

→ Ссылка