Как преобразовать мой предикат в универсальный предикат в C#?

#c# #asp.net-core #generics #predicate

Вопрос:

У меня есть код предиката, который отлично работает, но я хочу сократить свой код, преобразовать его в универсальный и использовать в своем репозитории

 public abstract class RequestParameters
{
    const int maxPageSize = 5;
    public int PageNumber { get; set; } = 1;

    private int _pageSize = 10;
    public int PageSize
    {
        get
        {
            return _pageSize;
        }
        set
        {
            _pageSize = (value > maxPageSize) ? maxPageSize : value;
        }
    }
    public string OrderBy { get; set; }
    public string Fields { get; set; }
}
 

а это моя модель поиска сотрудников

 public class EmployeeSearchModel : RequestParameters
{
    public EmployeeSearchModel()
    {
    
        OrderBy = "name";
    }       

    public string Name { get; set; }
    public int Age { get; set; }
    public string Position { get; set; }

    public Expression<System.Func<Employee, bool>> Go()
    {
        var predicate = PredicateBuilder.True<Employee>();

        if (!string.IsNullOrEmpty(Name))
        {
            predicate = predicate.And(i => i.Name.ToLower().Contains(Name.ToLower()));
        }
        if (!string.IsNullOrEmpty(Position))
        {
            predicate = predicate.And(i => i.Position.ToLower().Contains(Position.ToLower()));
        }
        return predicate;
    }
}
 

и это мое хранилище

 public async Task<PagedList<Employee>> GetEmployeesAsyncExp(EmployeeSearchModel employeeParameters)
    {
        Expression f = employeeParameters.Go();

        var employees = await FindByCondition(employeeParameters.Go())
        .OrderBy(e => e.Name)           
        .ToListAsync()....
 

….

и теперь я хочу изменить его на что-то общее, как это:

 public class EmployeeSearchModel<T> : RequestParameters ...
 

Комментарии:

1. Как универсальная версия должна знать свойство Name или Position для универсального типа? В какой-то момент вы должны выполнить свое условие поиска.

Ответ №1:

Ну, здесь нет реального вопроса, и много недостающей информации, а также дезинформации. Я предполагаю, что вы захотите заменить класс «Сотрудник», чтобы также фильтровать другие классы с помощью вашей части SearchModel. Поэтому «EmployeeSearchModel» неверен, так как он все еще содержит Сотрудника.

С другой стороны, ваш метод использует некоторые специфические для сотрудника части, такие как Имя или Возраст, и если ваши другие классы также не содержат Возраст и имя, это не может использоваться в качестве универсального метода.

Лучшее, что вы можете сделать для повторного использования вашей поисковой модели, — это изменить ее на абстрактный класс и позволить EmployeeSearchModel наследовать это:

 public abstract class SearchModel<T> : RequestParameters
{
    public abstract Expression<System.Func<T, bool>> Go();
}

public class EmployeeSearchModel : SearchModel<Employee>
{
    public EmployeeSearchModel()
    {

        OrderBy = "name";
    }

    public string Name     { get; set; }
    public int    Age      { get; set; }
    public string Position { get; set; }

    public override Expression<Func<Employee, bool>> Go()
    {
        var predicate = PredicateBuilder.True<Employee>();

        if (!string.IsNullOrEmpty(Name))
        {
            predicate = predicate.And(i => i.Name.ToLower().Contains(Name.ToLower()));
        }
        if (!string.IsNullOrEmpty(Position))
        {
            predicate = predicate.And(i => i.Position.ToLower().Contains(Position.ToLower()));
        }
        return predicate;
    }
}
 

Затем вы можете использовать его также для поиска с другими классами, такими как компания:

 public class CompanySearchModel : SearchModel<Company>
{
    public string Department {get; set;}

    public override Expression<Func<Company, bool>> Go()
    {
        var predicate = PredicateBuilder.True<Company>();

        if (!string.IsNullOrEmpty(Department))
        {
            predicate = predicate.And(x => x.Department.ToLower().Contains(Department.ToLower()));
        }
    }
}
 

Комментарии:

1. основная проблема заключается в следующем коде: открытое выражение переопределения<Функция<Сотрудник, bool><Сотрудник, bool>> Go() { …. потому что, если класс имеет много свойств, я должен повторить этот код : если (!строка. IsNullOrEmpty(Имя)) { предикат = предикат. И(я => i.Name. ToLower().Содержит(Имя. ToLower())); } итак, есть ли способ получить список свойств внутри класса, а затем с помощью цикла выполнить проверку и назначить эту строку if (!). IsNullOrEmpty(Отдел)) { предикат = предикат.И(x => x.Отдел. ТоЛоуэр().Продолжение……

2. @Ali Вы можете использовать отражение и метод GetProperties. Есть несколько вопросов по stackoverflow, просто найдите его. Я не проверял его, и он может не работать с предикатом, но вот фрагмент, который получает все общедоступные свойства типа string: var props = GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); for (var i = 0; i < props.Length; i ) { if (props[i].PropertyType == typeof(string) amp;amp; props[i].GetGetMethod(true).IsPublic amp;amp; props[i].CanRead) { // do your predicate logic here } }

3. так как я могу использовать реквизиты после IF как : предикат = предикат. И(я => i.Name. ToLower().Содержит(Имя. ToLower ())); ?

4. Вы можете оценить это так: props[i].GetValue(yourClass, null).ToString().ToLower() == compareableText.ToLower()