2009-04-23 5 views
27

Mam tablicę o zmiennej wielkości łańcuchów i próbuję programowo przechodzić przez tablicę i dopasowywać wszystkie wiersze w tabeli, w której kolumna "Tagi" zawiera co najmniej jeden z ciągów w tablicy. Oto niektóre pseudo kod:Jak dynamicznie dodawać operator OR do klauzuli WHERE w LINQ

IQueryable<Songs> allSongMatches = musicDb.Songs; // all rows in the table 

mogę łatwo kwerendy to filtrowanie tabeli na stałym zestawem strun, tak:

allSongMatches=allSongMatches.Where(SongsVar => SongsVar.Tags.Contains("foo1") || SongsVar.Tags.Contains("foo2") || SongsVar.Tags.Contains("foo3")); 

Jednak to nie działa (pojawia się następujący błąd: „wyrażenie lambda z ciałem oświadczenie nie mogą być konwertowane do drzewa wyrażenie”)

allSongMatches = allSongMatches.Where(SongsVar => 
    { 
     bool retVal = false; 
     foreach(string str in strArray) 
     { 
     retVal = retVal || SongsVar.Tags.Contains(str); 
     } 
     return retVal; 
    }); 

Czy ktoś może mi pokazać właściwą strategię, aby osiągnąć ten cel? Nadal jestem nowy w świecie LINQ :-)

Odpowiedz

32

Można użyć klasy PredicateBuilder:

var searchPredicate = PredicateBuilder.False<Songs>(); 

foreach(string str in strArray) 
{ 
    var closureVariable = str; // See the link below for the reason 
    searchPredicate = 
    searchPredicate.Or(SongsVar => SongsVar.Tags.Contains(closureVariable)); 
} 

var allSongMatches = db.Songs.Where(searchPredicate); 

LinqToSql strange behaviour

+0

Thanks Mehrdad, rozwiązanie przyszło w rekordowym czasie i pracował jak urok! Nie wiedziałem o klasie PredicateBuilder. Co za przydatna funkcja! Doceniam również twój komentarz na temat użycia zmiennej tymczasowej do przytrzymywania strun ... która doprowadziłaby mnie do szału! Victor –

+0

Nie ma za co! –

+0

Mam Confused o fałszywym wyrażenie, aby utworzyć var ​​searchPredicate, To musi być fałszywe? czy cokolwiek innego by pasowało? (Mówiąc o PredicateBuilder.False ()) – Daniel

0

Albo zbudować Expression<T> siebie, albo spojrzeć na inną drogą.

Założenie possibleTags to zbiór tagów, możesz użyć zamknięcia i dołączyć, aby znaleźć dopasowania. Powinno to znaleźć dowolne utwory z co najmniej jednym tagiem w możliwych tagach:

allSongMatches = allSongMatches.Where(s => (select t from s.Tags 
              join tt from possibleTags 
               on t == tt 
              select t).Count() > 0) 
+0

Hej Richard, doceniam twoją opinię . Z jakiegoś powodu VS nie spodobał się składni zaproponowanego kodu ... Czy brakuje mi czegoś oczywistego? –

+0

Całkiem możliwe, że wystąpił błąd ... potrzebowałoby trochę czasu, aby połączyć dane/testowe wiązki w celu potwierdzenia. Ale dodałem klauzulę select do wyrażenia wewnętrznego zrozumienia ... które jest wymagane (ups). – Richard

0

Istnieje inna, nieco łatwiejsza metoda, która to osiągnie. Blog ScottGu wyszczególnia dynamiczną bibliotekę linq, która okazała się bardzo pomocna w przeszłości. Zasadniczo, to generuje zapytanie z ciągiem Ci przepustkę Oto przykładowy kod chcesz napisać.

Dim Northwind As New NorthwindDataContext 

Dim query = Northwind.Products _ 
        .Where("CategoryID=2 AND UnitPrice>3") _ 
        .OrderBy("SupplierId") 

Gridview1.DataSource = query 
Gridview1.DataBind() 

Więcej informacji można znaleźć na blogu scottgu za here.

+0

nie wiesz, dlaczego to -1, wygląda uzasadnioną odpowiedź – piris

1

Niedawno stworzył metodę rozszerzenia do tworzenia wyszukiwań ciągów, które pozwalają również na przeszukiwanie OR. Blogu o here

ja też stworzył go jako pakiet Nuget które można zainstalować:

http://www.nuget.org/packages/NinjaNye.SearchExtensions/

Raz zainstalowany będzie można wykonać następujące

var result = db.Songs.Search(s => s.Tags, strArray); 

Jeśli chcesz utworzyć własną wersję, aby umożliwić powyższe, musisz wykonać następujące czynności:

public static class QueryableExtensions 
{ 
    public static IQueryable<T> Search<T>(this IQueryable<T> source, Expression<Func<T, string>> stringProperty, params string[] searchTerms) 
    { 
     if (!searchTerms.Any()) 
     { 
      return source; 
     } 

     Expression orExpression = null; 
     foreach (var searchTerm in searchTerms) 
     { 
      //Create expression to represent x.[property].Contains(searchTerm) 
      var searchTermExpression = Expression.Constant(searchTerm); 
      var containsExpression = BuildContainsExpression(stringProperty, searchTermExpression); 

      orExpression = BuildOrExpression(orExpression, containsExpression); 
     } 

     var completeExpression = Expression.Lambda<Func<T, bool>>(orExpression, stringProperty.Parameters); 
     return source.Where(completeExpression); 
    } 

    private static Expression BuildOrExpression(Expression existingExpression, Expression expressionToAdd) 
    { 
     if (existingExpression == null) 
     { 
      return expressionToAdd; 
     } 

     //Build 'OR' expression for each property 
     return Expression.OrElse(existingExpression, expressionToAdd); 
    } 
} 

Alternatywnie przyjrzeć projektu github dla NinjaNye.SearchExtensions jak to ma inne opcje i został refactored się nieco, aby umożliwić innych kombinacjach