2010-06-15 4 views
5

Wir haben ein Projekt mit LINQ to SQL, für die ich ein paar Suchseiten umschreiben muss, damit der Client wählen kann, ob sie eine und oder eine durchführen möchten oder suchen.Linq PredicateBuilder mit bedingten AND, OR und NOT Filter

Ich dachte über das Wiederholen der LINQ-Abfragen, die PredicateBuilder verwenden, und habe das funktioniert sehr gut, denke ich. Ich habe tatsächlich eine Klasse meine Prädikate enthalten, zB:

internal static Expression<Func<Job, bool>> Description(string term) 
{ 
    return p => p.Description.Contains(term); 
} 

Um die Suche durchführen Ich tue dies (einige Code der Kürze halber weggelassen):

public Expression<Func<Job, bool>> ToLinqExpression() 
{ 
    var predicates = new List<Expression<Func<Job, bool>>>(); 
    // build up predicates here 

    if (SearchType == SearchType.And) 
    { 
     query = PredicateBuilder.True<Job>(); 
    } 
    else 
    { 
     query = PredicateBuilder.False<Job>(); 
    } 

    foreach (var predicate in predicates) 
    { 
     if (SearchType == SearchType.And) 
     { 
      query = query.And(predicate); 
     } 
     else 
     { 
      query = query.Or(predicate); 
     } 
    } 
    return query; 
} 

Während ich mit diesem recht zufrieden bin Ich habe zwei Bedenken:

  1. Die If/Else-Blöcke, die eine SearchType-Eigenschaft bewerten fühlen, wie sie ein potenzieller Code Geruch sein könnten.
  2. Der Client besteht nun darauf, in der Lage zu sein, 'und nicht'/'oder nicht' zu suchen.

zu adressieren Punkt 2, ich denke ich, das meine Ausdrücke durch einfaches Umschreiben tun könnte, zum Beispiel:

internal static Expression<Func<Job, bool>> Description(string term, bool invert) 
{ 
    if (invert) 
    { 
     return p => !p.Description.Contains(term); 
    } 
    else 
    { 
     return p => p.Description.Contains(term); 
    } 
} 

Allerdings fühlt sich das wie ein bisschen eine Flickschusterei, die in der Regel bedeutet, dass es sich als eine bessere Lösung Dort. Kann jemand empfehlen, wie dies verbessert werden könnte? Ich bin mir bewusst, dass LINQ dynamisch ist, aber ich möchte LINQs starke Typisierung nicht wirklich verlieren.

Antwort

8

Wenn Sie weniger Linien suchen, können Sie die if/else mit ternären Operator ersetzen:

query = SearchType == SearchType.And ? PredicateBuilder.True<Job>() : PredicateBuilder.False<Job>(); 

    foreach (var predicate in predicates) 
    { 
     query = SearchType == SearchType.And ? query.And(predicate) : query.Or(predicate); 
    } 

für den 'and not'/'or not' Teil der ! Bediener den Trick tun sollten.

PD: Haben testen Sie die foreach Teil die Prädikate richtig einstellen ?, so weit ich erinnere Sie bauen den Ausdruck, der zu einem späteren Zeitpunkt ausgeführt werden, so können Sie eine Literalverweis haben nur Das letzte Satzprädikat in der letzten Iteration. Aus diesem Grund müssen Sie eine temporäre Variable verwenden, um den Wert jeder Iteration zu speichern.

EDIT: Wenn Sie einen Ausdruck programmatisch negieren wollen, ist das so eine Sache, Sie so etwas wie versuchen:

internal static Expression<Func<Job, bool>> Description(string term, bool invert) 
     { 
      return NegateExp<Func<Job, bool>>(p => p.Description.Contains(term), invert); 
     } 

Und die NegateExp Methode wird so etwas wie:

public static Expression<TDelegate> NegateExp<TDelegate>(Expression<TDelegate> expression, bool inverse) 
     { 
      if (inverse) 
      { 
       return Expression.Lambda<TDelegate>(Expression.Not(expression.Body), expression.Parameters); 
      } 
      return expression; 
     } 

Sie können einen Blick auf diese Frage für weitere Beispiele Is there any way to negate a Predicate?

+0

Ich habe Profilierung der Gen. Erated SQL, und alles sieht gut aus. Fairer Kommentar über die Verwendung von ternären Operatoren, obwohl ich mehr auf die Duplizierung in meinen aktualisierten Prädikaten bedacht bin. – richeym

+0

mit Expression.Not mit einem Delegaten sollte hier nützlich sein. Oben ein kleines Beispiel hinzugefügt. – JOBG

+0

Das ist eine interessante, wird es morgen versuchen.Es kam mir auch in den Sinn, dass ich den Ausdruck einfach mit dem invert-Parameter EXKLUSIVIEREN könnte - Es funktioniert, aber es erzeugt ziemlich ineffizientes SQL. – richeym

Verwandte Themen