Да, ты можешь. В одном подходе, который я использовал, в качестве поискового фильтра используется объект, который совпадает с вашим типом возвращаемого значения. Итак, если вы хотите выполнить поиск по имени клиента "Билл", вы установите Order.Customer.Name
на Билл. При передаче этого объекта методу применяются все применимые поисковые запросы.
Для этого начните с определения списка доступных для поиска полей:
Field<Order>[] Fields;
Заполните их, объявив новые поля:
var newField = new Field<Order>(o => o.Customer.Name, true, "Customer Name");
Параметр «true» означает, что он будет действовать как поле сортировки результатов.
Объект Field
содержит достаточно информации, чтобы позже сгенерировать выражения. Это будет выглядеть так:
public class Field<T>
{
public Field(Expression<Func<T, object>> field, bool sortField = false, string displayName = null)
{
//get & validate member
MemberExp = field.Body is UnaryExpression ? ((UnaryExpression)field.Body).Operand as MemberExpression
: (MemberExpression)field.Body;
Field = MemberExp?.Member;
if (Field == null) throw new ArgumentException("Field expression is not a member.");
//set field type
switch (Field.MemberType)
{
case MemberTypes.Property:
PropertyInfo p = (PropertyInfo)Field;
FieldType = p.PropertyType;
break;
case MemberTypes.Field:
FieldInfo f = (FieldInfo)Field;
FieldType = f.FieldType;
break;
default:
throw new Exception("Unsupported member type detected.");
}
//store input values
FieldExpression = field;
SortField = sortField;
DisplayName = displayName ?? Field.Name;
}
public bool SortField { get; set; }
public string DisplayName { get; private set; }
public MemberExpression MemberExp { get; private set; }
public Expression<Func<T, object>> FieldExpression { get; private set; }
public Func<T, object> GetValue => FieldExpression.Compile();
public Type FieldType { get; set; }
/// <summary>
/// Gets the full field name, i.e o => o.Customer.CustomerName returns "Customer.CustomerName"
/// </summary>
public string UnqualifiedFieldName
{
get
{
var stringExp = MemberExp.ToString();
var paramEnd = stringExp.IndexOf('.') + 1;
return stringExp.Substring(paramEnd);
}
}
}
После того, как вы определили все доступные для поиска поля, вы вызовете метод для получения результатов поиска на основе поисковых фильтров (T
), собранных вами от пользователя:
//get the results in ascending order, 10 items per page, first page
var results = GetSearchResults(searchFilters, "ASC", 10, 1);
Этот метод потребует, чтобы у вас была запрашиваемая коллекция данных. Я предполагаю, что у вас есть какой-то метод, например context.GetCollection()
, который извлекает ваши данные. Метод GetSearchResults
будет выглядеть так:
//Returns a filtered dataset based on provided search filters
//searchFilters is an object T which contains the search filters entered.
private List<T> GetSearchResults(T searchFilters, string sortDir = "ASC", int pageSize, int currentPage)
{
IQueryable<T> searchResults = context.GetCollection(); //get your data context here
var filterExpressions = new List<Expression<Func<T, bool>>>();
//Add filters
foreach (var field in Fields)
{
//try to get the search value, ignoring null exceptions because it's much harder
//to check for null objects at multiple levels. Instead the exception tells us there's
//no search value
string searchValue = null;
try
{
searchValue = field.GetValue(searchFilters)?.ToString();
}
catch (NullReferenceException) { }
if (string.IsNullOrWhiteSpace(searchValue)) continue;
//shared expression setup
ParameterExpression param = field.FieldExpression.Parameters.First();
Expression left = field.FieldExpression.Body;
ConstantExpression right = Expression.Constant(searchValue);
Expression body = null;
//create expression for strings so we can use "contains" instead of "equals"
if (field.FieldType == typeof(string))
{
//build the expression body
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
body = Expression.Call(left, method, right);
}
else
{ //handle expression for all other types
body = Expression.Equal(left, right);
}
//finish expression
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(body, param);
filterExpressions.Add(lambda);
}
//apply the expressions
searchResults = filterExpressions.Aggregate(searchResults, (current, expression) => current.Where(expression));
//get sort field
Field<T> sortField = Fields.FirstOrDefault(f => f.SortField);
searchResults = searchResults.OrderBy($"{sortField.UnqualifiedFieldName} {sortDir}");
// Get the search results
int count = searchResults.Count();
int maxPage = count / pageSize;
if (maxPage * pageSize < count) maxPage++;
if (currentPage > maxPage) currentPage = maxPage;
int skip = Math.Max(0, (filters.page - 1) * pageSize);
int display = Math.Max(0, Math.Min(count - skip, pageSize));
return searchResults.Skip(skip).Take(display).ToList();
}
Этот метод использует ваш Field[]
массив для построения выражений для ваших критериев и применения их к набору данных.
Надеюсь, это поможет! Дайте знать, если у вас появятся вопросы.
person
Daniel
schedule
11.07.2016