LINQ, как запросить, если значение находится между списком диапазонов? - PullRequest
4 голосов
/ 28 октября 2010

Допустим, у меня есть запись Person в базе данных, и есть поле Age для человека.

Теперь у меня есть страница, которая позволяет мне фильтровать людей в определенных возрастных диапазонах.

Например, я могу выбрать несколько вариантов выбора диапазона, таких как «0-10», «11-20», «31-40».

Так что в этом случае я бы вернул список людей от 0 до 20, а также от 30 до 40, но не 21-30.

Я взял возрастные диапазоны и заполнил список диапазонов, который выглядит следующим образом:

class AgeRange
{ 
     int Min { get; set; }
     int Max { get; set; }
}

List<AgeRange> ageRanges = GetAgeRanges();

Я использую LINQ to SQL для доступа к базе данных и запросов, но не могу понять, как запросить диапазоны.

Я хочу сделать что-то подобное, но, конечно, это не сработает, так как я не могу запросить свои локальные значения по значениям SQL:

var query = from person in db.People 
            where ageRanges.Where(ages => person.Age >= ages.Min && person.Age <= ages.Max).Any())
            select person;

Ответы [ 3 ]

10 голосов
/ 28 октября 2010

Вы можете построить предикат динамически с помощью PredicateBuilder:

static Expression<Func<Person, bool>> BuildAgePredicate(IEnumerable<AgeRange> ranges)
{
    var predicate = PredicateBuilder.False<Person>();
    foreach (var r in ranges)
    {
        // To avoid capturing the loop variable
        var r2 = r;
        predicate = predicate.Or (p => p.Age >= r2.Min && p.Age <= r2.Max);
    }
    return predicate;
}

Затем вы можете использовать этот метод следующим образом:

var agePredicate = BuildAgePredicate(ageRanges);
var query = db.People.Where(agePredicate);
0 голосов
/ 28 октября 2010

Благодаря ответу Томаса я смог создать более общую версию, которая, кажется, работает:

   static IQueryable<T> Between<T>(this IQueryable<T> query, Expression<Func<T, decimal>> predicate, IEnumerable<NumberRange> ranges)
    {
        var exp = PredicateBuilder.False<T>();

        foreach (var range in ranges)
        {
            exp = exp.Or(
                    Expression.Lambda<Func<T, bool>>(Expression.GreaterThanOrEqual(predicate.Body, Expression.Constant(range.Min)), predicate.Parameters))
                    .And(Expression.Lambda<Func<T, bool>>(Expression.LessThanOrEqual(predicate.Body, Expression.Constant(range.Max)), predicate.Parameters));
        }

        return query.Where(exp);
    }
0 голосов
/ 28 октября 2010

В качестве одной из упомянутых вами ошибок вы можете использовать только локальную последовательность с методом «Contains». Тогда можно было бы создать список всех разрешенных возрастов, например:

    var ages = ageRanges
        .Aggregate(new List<int>() as IEnumerable<int>, (acc, x) => 
            acc.Union(Enumerable.Range(x.Min,x.Max - (x.Min - 1)))
        );

Тогда вы можете позвонить:

People.Where(x => ages.Contains(x.Age))

Слово предостережения к этому рассказу, если ваши диапазоны будут большими, то это НЕ будет!

(Это будет хорошо работать для небольших диапазонов (ваше максимальное число принятых возрастов, вероятно, никогда не превысит 100), но даже больше, чем эта, и обе вышеуказанные команды станут ОЧЕНЬ дорогими!)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...