2010-10-28 140 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); 
    } 
相關問題