The quick-and-dirty way is to use a switch
with predefined property names. I don't recommend allowing users to specify the exact property or column to filter by because that will introduce privacy risks and security holes (e.g. as it would allow users to search for other users by email, name, or password without you realising it).
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;
}
Using this approach, you can avoid the ArgumentOutOfRangeException
in normal flow by using an enum
instead, which can be a route parameter (and also support user-defined ordering too!):
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." );
}
}
}
And these extension methods can be used like so:
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;
}