Используя пространство имен System.Linq.Dynamic
, я могу создать общий список столбцов для поиска на основе столбцов, присутствующих в текущем пользовательском элементе управления (сетка с возможностью поиска, которую мы используем в разных местах). Процесс прост: возьмите список столбцов, видимый текущему пользователю, добавьте столбцы в выражение динамического запроса в предложении where, посмотрите, содержит ли вся объединенная последовательность указанную строку.
Этим достигается 2 вещи, позволяя пользователю выполнять поиск с помощью единого поля поиска (стиль Google), которое работает во всех сетках, которые пользователь видит одинаково, а также выгружая поиск в базу данных.
Вот как это работает сейчас (результат = IQueryable<T
> или IEnumerable<T>
):
var se = GetGridSearchExpression(grid);
if (se != null) result = result.Where(se, grid.SearchText.ToLower());
private static string GetGridSearchExpression(Grid grid)
{
if (grid.SearchText.IsNullOrEmpty()) return null;
var sb = new StringBuilder();
foreach (var s in grid.ColumnNames)
{
sb.AppendFormat("{0} {1} ",
sb.Length == 0 ? string.Empty : "+\"|^|\"+", s);
}
return string.Format("({0}).ToLower().Contains(@0)", sb);
}
Напечатанный "| ^ |" строка - это что-то случайное, чтобы предотвратить поиск в одном столбце, чтобы он соответствовал следующему, например столбцы «Bo» «Bryant» от соответствия поиску «Bob», результат поиска по запросу «Bo | ^ | Bryant» предотвращает совпадение.
Обнуляемые значения - вот где возникают проблемы, имея DateTime? или тип Nullable, например, приводит к следующей ошибке:
Expression of type 'System.Nullable`1[System.DateTime]' cannot be used for
parameter of type 'System.Object' of method
'System.String Concat(System.Object, System.Object)'
Вот часть DynamicQueryable, которая взорвалась:
Expression GenerateStringConcat(Expression left, Expression right) {
return Expression.Call(null,
typeof (string).GetMethod("Concat", new[] {typeof (object), typeof (object)}),
new[] {left, right});
}
Единственный способ, который я нашел до сих пор, - это заменить добавление в построителе выражений на:
foreach (var s in grid.ColumnNames)
{
sb.AppendFormat("{0}({1} != null ? {1}.ToString() : string.Empty)",
sb.Length == 0 ? string.Empty : "+\"|^|\"+", s);
}
Поскольку мы находимся в LINQ to SQL, это приводит к раздутому оператору case. Учитывая альтернативу загрузки каждого объекта из БД для последующего поиска, можно использовать оператор case максимум для 8-10 столбцов.
Есть ли более чистый или простой способ сделать все или часть этого?
Отредактировано: Спасибо, Марк ... Я никогда не использую GetEnumerator где-либо в моем коде, всегда foreach или .ForEach () ... но по какой-то причине в то время это облегчило отладку, хотя я могу ' Теперь не помню почему. Решил вопрос по актуальному коду.
foreach
- слишком легко ошибиться; например, вам не удалось избавиться от счетчика. Просто используйтеforeach
- person Marc Gravell   schedule 22.09.2009