У меня есть сайт MVC ASP.NET (который использует Linq Для Sql для ORM), и ситуация, где клиент хочет поисковое средство против сделанной на заказ базы данных, посредством чего они могут выбрать любому, делают 'И' поиск (все соответствие критериев) или 'ИЛИ' поиск (любое соответствие критериев). Запрос довольно сложен и долог, и я хочу знать, существует ли простой способ, которым я могу сделать, он сделать и не имея необходимость иметь создает и поддерживает две различных версии запроса.
Например, ток 'И' поисковый выглядит примерно так (но это - очень упрощенная версия):
private IQueryable<SampleListDto> GetSampleSearchQuery(SamplesCriteria criteria)
{
var results = from r in Table where
(r.Id == criteria.SampleId) &&
(r.Status.SampleStatusId == criteria.SampleStatusId) &&
(r.Job.JobNumber.StartsWith(criteria.JobNumber)) &&
(r.Description.Contains(criteria.Description))
select r;
}
Я мог скопировать это и заменить && || операторы для получения 'ИЛИ' версия, но чувство, там должен быть лучший способ достигнуть этого. У кого-либо есть какие-либо предложения, как это может быть достигнуто эффективным и гибким способом, который легко поддержать?Спасибо.
Вы можете создать метод расширения в соответствии со строками
public static IQueryable<T> BoolWhere<T>(this IQueryable<T> source, Expression<Func<T, TValue>> selector, bool isOr) {
//use isOr value to determine what expression to build and add to the source
}
, где isOr будет определять, использовать ли выражение «and» или выражение «или». Затем вы можете построить свой запрос в соответствии со строками
bool isOr = true; //or false
var results = Data.BoolWhere(r => r.Id == criteria.SampleId, isOr)
.BoolWhere(r => r.Status.SampleStatusId == criteria.SampleStatusId, isOr)
.BoolWhere(r => r.Job.JobNumber.StartsWith(criteria.JobNumber), isOr)
.BoolWhere(r => r.Description.Contains(criteria.Description), isOr)
Возможно, проще, чем идея Йенса, визуализировать, если вы ищете только объединенное ИЛИ и объединенное И (а не какое-то сочетание), вы всегда можете выразить свое равенство в виде списка тестов, а затем применить операторы Any или All к нему. Например:
var queries = new List<Func<Table,SampleListDto,bool>>{
((a,b) => a.Id == b.SampleId),
((a,b) => a.Status.SampleStatusId == b.SampleStatusId),
((a,b) => a.Job.JobNumber.StartsWith(b.JobNumber)),
((a,b) => a.Description.Contains(b.Description))
};
var results = Table.Where(t=> queries.All(q => q(t, criteria)); // returns the && case
// or: var results = Table.Where(t=>queries.Any(q=>q(t,criteria));
Как и с Йенсом, я не знаю, насколько эффективно это переводится в SQL, но если это станет проблемой, вам, вероятно, все равно лучше преобразовать его в собственный SQL.
Вот информация о PredicateBuilder
. Он должен быть совместим с LINQ to SQL.
Можно создать новую функцию для использования функций And или Or в PredicateBuilder:
private IQueryable<SampleListDto> GetSampleSearchQuery(
SamplesCriteria criteria,
Func<Expression<Func<SampleListDto, bool>>,
Expression<Func<SampleListDto, bool>>,
Expression<Func<SampleListDto, bool>>> logicExpr)
{
var results = from r in Table where
logicExpr(r => r.Id == criteria.SampleId,
logicExpr(r => r.Status.SampleStatusId == criteria.SampleStatusId,
logicExpr(r => r.Job.JobNumber.StartsWith(criteria.JobNumber),
logicExpr(r => r.Description.Contains(criteria.Description)))))
select r;
}
Функции And и Or будут выглядеть так:
private IQueryable<SampleListDto> GetOrSampleSearchQuery(
SamplesCriteria criteria)
{
return GetSampleSearchQuery(criteria, PredicateBuilder.Or<SampleListDto>);
}
private IQueryable<SampleListDto> GetAndSampleSearchQuery(
SamplesCriteria criteria)
{
return GetSampleSearchQuery(criteria, PredicateBuilder.And<SampleListDto>);
}
Если у вас есть эти методы расширения:
public static class BoolExtensions
{
public static bool And<TR, TC>(this IEnumerable<Func<TR, TC, bool>> statements, TR value, TC criteria)
{
foreach (var statement in statements)
{
if (!statement.Invoke(value, criteria))
{
return false;
}
}
return true;
}
public static bool Or<TR, TC>(this IEnumerable<Func<TR, TC, bool>> statements, TR value, TC criteria)
{
foreach (var statement in statements)
{
if (statement.Invoke(value, criteria))
{
return true;
}
}
return false;
}
}
Тогда вы можете объявить ваши утверждения как список:
List<Func<TypeOfR, TypeOfC, bool>> statements = new List<Func<TypeOfR, TypeOfC, bool>>()
{
{ (r, c) => r.Id == c.SampleId },
{ (r, c) => r.Status.SampleStatusId == c.SampleStatusId },
...
};
И написать ваш запрос как:
var results = from r in Table where
statements.And(r, criteria)
select r;
или для ||
версии:
var results = from r in Table where
statements.Or(r, criteria)
select r;
и просто поддерживать утверждения в одном месте.