Dynami c Фильтрация LINQ - PullRequest
2 голосов
/ 07 мая 2020

Как создать динамический c поиск по списку с использованием другого поля из модели?

public class UserModel
    {
        public int UserId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string DisplayName
        {
            get {return $"{ LastName }, { FirstName }"; }            
        }
     }
// Sample search
private void Search(string fieldname, string searchString)
        {
            List<UserModel> data = Users.Where(x => x.fieldname == searchString).ToList();
        }

Ответы [ 3 ]

3 голосов
/ 07 мая 2020

Вы можете использовать параметр выражения:

using System.Linq.Expressions;

//...

private List<UserModel> Search<T>(Expression<Func<UserModel, T>> criteria) 
{
  return Users.Where(criteria).ToList();
}

Используется так:

var matches = model.Search(x => x.FirstName == "Bob");
1 голос
/ 07 мая 2020

Самый простой способ - использовать switch с предопределенными именами свойств. Я не рекомендую разрешать пользователям указывать точное свойство или столбец для фильтрации, потому что это приведет к рискам конфиденциальности и дырам в безопасности (например, поскольку это позволит пользователям искать других пользователей по электронной почте, имени или паролю без вашего ведома) .

private async Task<List<UserModel>> SearchUsersAsync( String fieldname, String value )
{
    IQueryable<User> q = this.Users;

    switch( fieldName )
    {
    case nameof(UserModel.Name):
        q = q.Where( u => u.Name == value );
        break;

    case nameof(UserModel.Bio):
        q = q.Where( u => u.Bio.Contains( value ) );
        break;

    case nameof(UserModel.Email):
        q = q.Where( u => u.Email == value );
        break;

    default:
        throw new ArgumentOutOfRangeException( "Unsupported property name." );
    }

    List<User> users = await q
        .OrderBy( u => u.UserId )
        .ToListAsync()
        .ConfigureAwait(false);

    List<UserModel> userViewModels = users
        .Select( u => UserModel.FromUserEntity( u ) )
        .ToList();

    return userViewModels;
}

Используя этот подход, вы можете избежать ArgumentOutOfRangeException в нормальном потоке, используя вместо него enum, который может быть параметром маршрута (а также поддерживать определяемое пользователем упорядочивание!):

enum UserProperty
{
    None,
    Name,
    Bio,
    Email
}

internal static class QueryExtensions
{
    public static IQueryable<User> WhereProperty( this IQueryable<User> q, UserProperty prop, String value )
    {
        if( String.IsNullOrWhiteSpace( value ) ) return q;

        switch( prop)
        {
        case UserProperty.None:
            return q;

        case UserProperty.Name:
            return q.Where( u => u.Name == value );

        caseUserProperty.Bio:
            return q.Where( u => u.Bio.Contains( value ) );

        case UserProperty.Email:
            return q.Where( u => u.Email == value );

        default:
            throw new ArgumentOutOfRangeException( "Unsupported property name." );
        }
    }

    public static IOrderedQueryable<User> OrderByProperty( this IQueryable<User> q, UserProperty prop, Boolean asc )
    {
        switch( prop )
        {
        case UserProperty.None:
            return q;

        case UserProperty.Name:
            return asc ? q.OrderBy( u => u.Name ) : q.OrderByDescending( u => u.Name );

        case UserProperty.Bio:
             return asc ? q.OrderBy( u => u.Bio ) : q.OrderByDescending( u => u.Bio );

        case UserProperty.Email:
             return asc ? q.OrderBy( u => u.Email ) : q.OrderByDescending( u => u.Email );

        default:
            throw new ArgumentOutOfRangeException( "Unsupported property name." );
        }
    }
}

И эти методы расширения можно использовать так:

private async Task<List<UserModel>> SearchUsersAsync( UserProperty filterProp = UserProperty.None, String filterValue = null, UserProperty sortProp = UserProperty.None, Boolean sortAscending = true )
{
    List<User> users = await this.Users
        .WhereProperty( filterProp, filterValue )
        .OrderByProperty( sortProp, sortAscending )
        .ToListAsync()
        .ConfigureAwait(false);

    List<UserModel> userViewModels = users
        .Select( u => UserModel.FromUserEntity( u ) )
        .ToList();

    return userViewModels;
}
0 голосов
/ 07 мая 2020

Я считаю, что словари полезны для хранения всего жесткого кода:

private Dictionary<string, Func<UserModel, string, bool>> _filters =
    new Dictionary<string, Func<UserModel, string, bool>>()
    {
        { "FirstName", (u, x) => u.FirstName == x },
        { "LastName", (u, x) => u.LastName == x },
        { "DisplayName", (u, x) => u.DisplayName == x },
    };

private void Search(string fieldname, string searchString)
{
    List<UserModel> data = Users.Where(u => _filters[fieldname](u, searchString)).ToList();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...