2009-08-04 11 views
6

Próbuję zrobić takiego porównania w oparciu o parametr zewnątrz (przekazany przez formularz wyszukiwania), który określa rodzaj porównania ("%string" lub "string%" lub "%string%")Linq, wyrażenia, NHibernate i lubią porównania

Myślałam w poniższym kierunek

query = query.Where(
    Entity.StringProperty.Like("SearchString", SelectedComparsionType) 
) 

jak metoda będzie niż na podstawie wybranego typu powrotu .StartsWith() lub .EndsWith() lub .SubString()

Moja wiedza na temat wyrażeń jest zdecydowanie daleka od doskonałości, ponieważ nie byłem w stanie skonstruować metody, która mogłaby dać właściwy wynik (porównanie po stronie serwera w SQL, podobnie jak w przypadku metody StartsWith).

Odpowiedz

17

Prosty sposób

Wystarczy użyć

if (comparison == ComparisonType.StartsWith) 
    query = query.Where(e => e.StringProperty.StartsWith("SearchString")); 
else if ... 

hard way

Jeśli chcesz zrobić coś takiego, albo upewnić się, że dostawca LINQ można powiedzieć o ta nowa metoda jakoś i jak przekłada się na SQL (mało prawdopodobne), lub uniemożliwić twojej metodzie kiedykolwiek dojście do dostawcy LINQ i dostarczyć dostawcy czegoś, co on rozumie (ha r & D). Na przykład, zamiast

query.Where(e => CompMethod(e.StringProperty, "SearchString", comparsionType)) 

można stworzyć coś podobnego

var query = source.WhereLike(e => e.StringProperty, "SearchString", comparsionType) 

z następującego kodu

public enum ComparisonType { StartsWith, EndsWith, Contains } 

public static class QueryableExtensions 
{ 
    public static IQueryable<T> WhereLike<T>(
     this IQueryable<T> source, 
     Expression<Func<T, string>> field, 
     string value, 
     SelectedComparisonType comparisonType) 
    { 
     ParameterExpression p = field.Parameters[0]; 
     return source.Where(
      Expression.Lambda<Func<T, bool>>(
       Expression.Call(
        field.Body, 
        comparisonType.ToString(), 
        null, 
        Expression.Constant(value)), 
      p)); 
    } 
} 

Można nawet dodać dodatkowe kryteria tędy

var query = from e in source.WhereLike(
       e => e.StringProperty, "SearchString", comparsionType) 
      where e.OtherProperty == 123 
      orderby e.StringProperty 
      select e; 

Bardzo, bardzo trudny sposób

Byłoby (technicznie) można przepisać drzewa wyrażenie zanim operator widzi, więc może użyć zapytania miał na myśli w pierwszej kolejności, ale ci” d musiał

  • stworzyć Where(this IQueryable<EntityType> source, Expression<Func<EntityType, bool>> predicate) przechwycić Queryable.Where,
  • przepisać drzewa wyrażenie, zastępując swoją CompMethod, gdziekolwiek to jest, z jednej z metod String,
  • wywołaj oryginalny Queryable.Where z nowym wyrażeniem,
  • , a przede wszystkim umiesz nadążać za powyższym rozszerzeniem w pierwszej kolejności!

Ale to chyba zbyt skomplikowane dla tego, co masz na myśli.

+0

Ruben: Fenomenalny, to piekielne wyjaśnienie - kompletne, zrozumiałe, dokładne i dokładnie to, czego szukałem, dziękuję. Chciałbym także podziękować innym za ich pomoc. –

-1

Lepiej zrób to, używając Regex, aby rozwiązać ten problem.

+0

Nie spowoduje to bazy danych LIKE operatora, który moim zdaniem OP chce – MattH

1

Brzmi jak powinno być chcąc użyć:

query = query.Where(
Entity.StringProperty.Contains("SearchString") 
) 

ten powinien map do:

WHERE StringProperty LIKE '%SearchString%' 

Powinno to również pracować dla bardziej zaawansowanych maski wyszukiwarek takich jak "Mr Sm% TH?" , ale nie musiałem jeszcze testować żadnych łańcuchów wyszukiwania takich jak ja.


UPDATE: Na podstawie OPS edycji

Brzmi jak co prosicie o coś jak poniżej:

public enum SelectedComparsionType 
    { 
     StartsWith, 
     EndsWith, 
     Contains 
    } 

public static bool Like(this string searchString, string searchPattern, SelectedComparsionType searchType) 
{ 
    switch (searchType) 
    { 
     case SelectedComparsionType.StartsWith: 
      return searchString.StartsWith(searchPattern); 
     case SelectedComparsionType.EndsWith: 
      return searchString.EndsWith(searchPattern); 
     case SelectedComparsionType.Contains: 
     default: 
      return searchString.Contains(searchPattern); 
    } 
} 

Pozwoliłoby to na pisanie kodu, jak wymaga , tj .:

query = query.Where(
Entity.StringProperty.Like("SearchString", SelectedComparsionType.StartsWith) 
) 

Jednak, osobiście, Zamieniłbym jakiekolwiek użycie SelectedComparsionType, z bezpośrednim wywołaniem do wymaganej funkcji ciągu. Np.

query = query.Where(
Entity.StringProperty.StartsWith("SearchString") 
) 

Ponieważ nadal będzie mapować zapytanie SQL "LIKE".

0

To jest dokładnie to, co miałem na myśli, dziękuję. Miałem już coś podobnego, ale nie przełożyło się to na SQL. Na przykład, jeśli pracował Zrobiłem to bezpośrednio:

Entity.StringProperty.EndsWith("SearchString"); 

To nie działa, jeśli użyłem specjalną metodę:

CompMethod("BaseString","SearchString",SelectedComparsionType.EndsWith) 

myślę, że prawdopodobnie ma coś wspólnego z oceną ekspresji, ja nie jestem pewna co.