Поиск по ряду критериев

Всем привет. Подскажите пожалуйста как можно сделать поиск с группой критериев. У меня есть таблица с информацией о человеке и еще парой свойств таких как телефон и дата. Как сделать так, чтобы фильтр переваривал все поля запроса сразу. Т.е. сделать поиск по ряду критериев.

Сейчас имеется такой код. Идею с Func подсмотрел где-то по форумам, но она у меня не срабатывает. В консоль ничего не падает.

public class GetBlackListQueryHandler : IRequestHandler<GetBlackListRequest, IOrderedEnumerable<BlackList>>
{
    readonly IReadonlyRepository<BlackListEntity> _readonlyRepository;
    public GetBlackListQueryHandler(
        IReadonlyRepository<BlackListEntity> readonlyRepository)
    {
        _readonlyRepository = readonlyRepository;
    }

    public async Task<IOrderedEnumerable<BlackList>> Handle(GetBlackListRequest request, CancellationToken cancellationToken)
    {
        IOrderedEnumerable<BlackList> x = null;
        var result = await _readonlyRepository.GetAsync(cancellationToken);
        var dataForSort = result.Value.Select(x => new BlackList
        {
            ItemId = x.Id,
            LastName = x.LastName,
            FirstName = x.FirstName,
            MiddleName = x.MiddleName,
            PhoneNumber = x.PhoneNumber,
            CreateDate = x.CreateDate,
        }).ToList();

        string? lastName = request.LastNameS,
            firstName = request.FirstNameS,
            middleName = request.MiddleNameS,
            phone = request.PhoneS;
        DateTime? date = null;

        bool filterByLastName = true,
            filterByFirstName = false,
            filterByMiddleName = false,
            filterByPhone = false,
            filterByDate = true;

        Func<BlackList, bool> predicateByLastName = x => x.LastName == lastName;
        Func<BlackList, bool> predicateByFirstName = x => x.FirstName == firstName;
        Func<BlackList, bool> predicateByMiddleName = x => x.MiddleName == middleName;
        Func<BlackList, bool> predicateByPhone = x => x.PhoneNumber == phone;
        Func<BlackList, bool> predicateByDate = x => x.CreateDate == date;

        Func<BlackList, bool> mainPredicate = x => (!filterByLastName || predicateByLastName(x))
                                                && (!filterByFirstName || predicateByFirstName(x))
                                                && (!filterByMiddleName || predicateByMiddleName(x))
                                                && (!filterByPhone || predicateByPhone(x))
                                                && (!filterByDate || predicateByDate(x));

        foreach (var entity in dataForSort.Where(mainPredicate))
        {
            Console.WriteLine(entity);
        }


        switch (request.SortBy)
        {
            case "LastName":
                x = dataForSort.OrderBy(x => x.LastName);
                break;
            case "LastNameDesc":
                x = dataForSort.OrderByDescending(x => x.LastName);
                break;
            case "FirstName":
                x = dataForSort.OrderBy(x => x.FirstName);
                break;
            case "FirstNameDesc":
                x = dataForSort.OrderByDescending(x => x.FirstName);
                break;
            case "MiddleName":
                x = dataForSort.OrderBy(x => x.MiddleName);
                break;
            case "MiddleNameDesc":
                x = dataForSort.OrderByDescending(x => x.MiddleName);
                break;
            case "PhoneNumber":
                x = dataForSort.OrderBy(x => x.PhoneNumber);
                break;
            case "PhoneNumberDesc":
                x = dataForSort.OrderByDescending(x => x.PhoneNumber);
                break;
            case "CreateDate":
                x = dataForSort.OrderBy(x => x.CreateDate);
                break;
            case "CreateDateDesc":
                x = dataForSort.OrderByDescending(x => x.CreateDate);
                break;
        }
        return x;
    }
}

BlackList моделька

public class BlackList
{
    public Guid ItemId { get; set; }
    public string? LastName { get; set; }
    public string? FirstName { get; set; }
    public string? MiddleName { get; set; }
    public string? PhoneNumber { get; set; }
    public DateTime CreateDate { get; set; }
}

BlackListRequest

  public string AccessToken { get; set; } = default!;
    public string SortBy { get; set; } = "CreateDate";
    public string? LastNameS { get; set; }
    public string? FirstNameS { get; set; }
    public string? MiddleNameS { get; set; }
    public string? DateS { get; set; }
    public string? PhoneS { get; set; }

Верия EF Core 6.0.6

Метод из репозитория

       Task<Result<IEnumerable<T>>> GetAsync(CancellationToken cancellationToken, 
        Expression<Func<T, bool>>? filter = null,
        Func<IQueryable<T>, IOrderedQueryable<T>>? sort = null, 
        string? include = null);

реализацию инклюда

 public static IQueryable<T> AddIncludes<T>(this IQueryable<T> query, string? include = null)
        where T : class
    {
        if (!string.IsNullOrWhiteSpace(include))
        {
            foreach (var i in include.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(i);
            }
        }

        return query;
    }

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

Автор решения: Alexander Petrov

Вызов метода репозитория может выглядеть следующим образом:

var result = await _readonlyRepository.GetAsync(
    cancellationToken,
    x => x.FirstName.StartsWith("A"),
    query => query.OrderBy(x => x.LastName),
    "Childs,Items");

Вначале идёт токен отмены, затем выражение фильтрации, затем выражение сортировки и последним - имя навигационного свойства-коллекции.

Вообще, CancellationToken всегда должен идти последним параметром.

Вы так и не показали определение GetBlackListRequest.
Судя по его использованию, код метода Handle может выглядеть так:

Expression<Func<BlackListEntity, bool>>? filter = null;

if (request.FirstNameS != null)
    filter = x => x.FirstName == request.FirstNameS;
else if (request.LastNameS != null)
    filter = x => x.LastName == request.LastNameS;

Func<IQueryable<BlackListEntity>, IOrderedQueryable<BlackListEntity>>? sort = null;

if (request.SortBy != null)
{
    string sortProperty = request.SortBy;
    if (sortProperty.EndsWith("Desc"))
    {
        sortProperty = sortProperty[..^4];
        sort = query => query.OrderByDescending(x => EF.Property<BlackListEntity>(x, sortProperty));
    }
    else
    {
        sort = query => query.OrderBy(x => EF.Property<BlackListEntity>(x, sortProperty));
    }
}

var result = await _readonlyRepository.GetAsync(
    cancellationToken, filter, sort, request.Include);

Мне неведомо, как именно планируется задавать и использовать фильтр. Поэтому лишь базовый пример.
Вы там пытаетесь конструировать mainPredicate - да, нужно что-то такое. Получается сложно. Вероятно, следует изменить GetBlackListRequest - это же ваша часть кода?

И в самом конце:

var dataForSort = result.Value
    .Select(x => new BlackList
    {
        // маппинг свойств
    })
    .ToList();

return dataForSort;
→ Ссылка