2010-10-28 136 views
4

假设我在数据库中有一个Person记录,并且该人员有一个Age字段。LINQ如何查询一个值是否在一个范围列表之间?

现在我有一个页面允许我筛选特定年龄段的人。

例如,我可以选择多个范围选择,例如“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到SQL对我的数据库访问和查询,但我无法弄清楚如何查询范围。

我想要做这样的事情,当然,这是不行的,因为我无法查询我的本地值对SQL值:与PredicateBuilder

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

您收到了什么确切的错误? – 2010-10-28 15:29:36

+0

NotSupportedException - 本地序列不能用于除Contains运算符之外的查询运算符的LINQ to SQL实现。 – Makotosan 2010-10-28 15:34:40

+0

为什么不能根据db值查询你的本地值?我认为你需要做的是在AgeRanges集合的循环内进行查询,并使用Linq来联合查询。我得看看我能否拿出一个例子,因为我从来没有这样做过......(我的意思是工会) – EJC 2010-10-28 15:37:02

回答

10

你可以建立谓词动态:

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

哦,很好。比我想象的要干净得多:) – EJC 2010-10-28 15:41:14

+0

当我运行它时,它似乎只返回年龄在最后指定日期范围但没有其他人的人。 – diceguyd30 2010-10-28 15:53:58

+0

虽然清洁+1!我一定会开始使用它! – diceguyd30 2010-10-28 16:07:16

0

作为你提到的错误之一,你只能用'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),但比这两者上述命令的多了会变得非常昂贵!)

+0

该解决方案的问题在于,对于大范围而言,它不适用。例如,假设我是按工资范围而不是年龄过滤的。 – Makotosan 2010-10-28 16:06:22

+0

确实。我对此感到满意,因为范围会相对较小(100件物品),但您绝对正确。任何其他域名,这可能是相当昂贵的操作!我会编辑一个警告。 – diceguyd30 2010-10-28 16:09:19

0

感谢托马斯的回答,我能够创建这个似乎正在工作的更通用的版本:

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); 
    } 
相关问题