LINQ к SQL - Как эффективно сделать или И или ИЛИ искать несколько критериев

У меня есть сайт 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;

}

Я мог скопировать это и заменить && || операторы для получения 'ИЛИ' версия, но чувство, там должен быть лучший способ достигнуть этого. У кого-либо есть какие-либо предложения, как это может быть достигнуто эффективным и гибким способом, который легко поддержать?Спасибо.

10
задан Dan Diplo 23 March 2010 в 15:07
поделиться

5 ответов

1
ответ дан 4 December 2019 в 01:56
поделиться

Вы можете создать метод расширения в соответствии со строками

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)
2
ответ дан 4 December 2019 в 01:56
поделиться

Возможно, проще, чем идея Йенса, визуализировать, если вы ищете только объединенное ИЛИ и объединенное И (а не какое-то сочетание), вы всегда можете выразить свое равенство в виде списка тестов, а затем применить операторы 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.

1
ответ дан 4 December 2019 в 01:56
поделиться

Вот информация о 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>);
}
1
ответ дан 4 December 2019 в 01:56
поделиться

Если у вас есть эти методы расширения:

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;

и просто поддерживать утверждения в одном месте.

2
ответ дан 4 December 2019 в 01:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: