LINQ zawiera rozróżnianie wielkości liter

174

W tym kodzie rozróżniana jest wielkość liter, jak sprawić, by wielkość liter nie była rozróżniana?

public IQueryable<FACILITY_ITEM> GetFacilityItemRootByDescription(string description)
{
    return this.ObjectContext.FACILITY_ITEM.Where(fi => fi.DESCRIPTION.Contains(description));
}
Jeaf Gilbert
źródło
Odpowiedzi Sjoerda są poprawne, ale ... Chcę uzyskać wyniki wyszukiwania nazwisk z tureckim İ (na przykład) podczas wpisywania i i odwrotnie. W tym przypadku ToLower wydaje się być właściwą drogą. Proszę popraw mnie jeżeli się mylę. O tureckim İ: en.wikipedia.org/wiki/Dotted_and_dotless_I
He Nrik
@HeNrik - Jak omówiono w Turcji Link do testu w komentarzu JYeltona pod zaakceptowaną odpowiedzią, po uruchomieniu z kulturą turecką te dwa i będą różne - więc nie znajdziesz imion z drugim i. Chcesz ToLowerInvariant. Zobacz dyskusję pod różnymi odpowiedziami tutaj .
ToolmakerSteve
to jest stare pytanie, ale warto zauważyć, że w obecnej wersji EF core 2.0 ToLower () działa w następujący sposób person.Where (p => p.Name.ToLower (). Contains (myParam.Name.ToLower () )); Używam tego w kwerendzie Linq przeciwko DB Postgres. Nie mam rozróżniania wielkości liter w sortowaniu kolumn w DB i sprawdziłem, że bez ToLower () dopasowanie jest wyraźnie rozróżniane.
shelbypereira

Odpowiedzi:

72

Zakładając, że pracujemy tutaj ze stringami, oto kolejne „eleganckie” rozwiązanie wykorzystujące IndexOf().

public IQueryable<FACILITY_ITEM> GetFacilityItemRootByDescription(string description)
{
    return this.ObjectContext.FACILITY_ITEM
        .Where(fi => fi.DESCRIPTION
                       .IndexOf(description, StringComparison.OrdinalIgnoreCase) != -1);
}
Jeff Mercado
źródło
7
Miły. Jednak do moich własnych celów nie działa to w przypadku LINQ to Entities. Dobre rozwiązanie dla LINQ to Objects.
Damian Powell
242
fi => fi.DESCRIPTION.ToLower().Contains(description.ToLower())
Nealv
źródło
49
Jak Jon Skeet skomentował powiązane pytanie , ta metoda nie przejdzie testu indyka .
JYelton
5
Nie, ale bazy danych działają na podstawie zestawów znaków i sortowania. Jeśli próbujesz przenieść pracę do bazy danych, musisz przyjąć pewne założenia dotyczące zestawu znaków i sortowania, prawda?
Christopher Stevenson,
66
Contains powinno używać IEqualityComparer<string>atrybutu do obsługi porównania. Użyj ToLower i ToUpper, aby sprawdzić równość, to zły pomysł. Spróbuj: .Contains(description, StringComparer.CurrentCultureIgnoreCase)na przykład
Dorival
19
Komentarz od @Dorival nie działa, ponieważ podaje następujący komunikat o błędzie:Error 1 'string' does not contain a definition for 'Contains' and the best extension method overload 'System.Linq.ParallelEnumerable.Contains<TSource>(System.Linq.ParallelQuery<TSource>, TSource, System.Collections.Generic.IEqualityComparer<TSource>)' has some invalid arguments
eMi
6
Containswith StringComparernie otrzymuje łańcucha jako parametru, więc będzie to błąd kompilacji. IndexOfon Queryableprawdopodobnie nie może zostać przetłumaczony na SQL. Osobiście uważam, że ta odpowiedź jest całkowicie ważna, ponieważ mówimy o LINQ do bazy danych.
Thariq Nugrohotomo
122

Jeśli zapytanie LINQ jest wykonywane w kontekście bazy danych, wywołanie Contains()jest mapowane na plikLIKE operatora:

.Where(a => a.Field.Contains("hello")) staje się Field LIKE '%hello%'. LIKEOperator nie uwzględnia wielkości liter domyślnie, ale które mogą być zmieniane przez zmianę sortowania kolumny .

Jeśli zapytanie LINQ jest wykonywane w kontekście .NET, możesz użyć IndexOf () , ale ta metoda nie jest obsługiwana w LINQ to SQL.

LINQ to SQL nie obsługuje metod, które przyjmują CultureInfo jako parametr, prawdopodobnie dlatego, że nie może zagwarantować, że serwer SQL obsługuje takie same kultury jak .NET. To nie jest do końca prawdą, ponieważ tak jest wsparciaStartsWith(string, StringComparison) .

Wydaje się jednak, że nie obsługuje ona metody, która LIKEw LINQ to SQL jest oceniana jako , ani do porównania niewrażliwego na wielkość liter w .NET, co uniemożliwia wykonanie Contains () bez uwzględniania wielkości liter w spójny sposób.

Sjoerd
źródło
Tylko FYI EF 4.3 nie obsługuje StartsWith. Otrzymuję: LINQ to Entities nie rozpoznaje metody `` Boolean StartsWith (System.String, System.StringComparison) ''
nakhli
StartWith konwertuje na LIKE 'witaj%'?
Bart Calixto
link clicdata jest martwy.
Adam Parkin
2
wielki wysiłek, aby
zagłębić się
1
Więc jakie są te opcje podczas korzystania z EF? W jednym kontekście muszę insensitivewyszukiwać przypadki , aw drugim potrzebuję, aby tak było case sensitive. Czy muszę po prostu przejmować wydajność i używać funkcji „toLower ()”?
Zapnologica
12

Przyjęta tutaj odpowiedź nie wspomina o fakcie, że jeśli masz pusty ciąg ToLower (), zgłosi wyjątek. Bezpieczniejszym sposobem byłoby:

fi => (fi.DESCRIPTION ?? string.Empty).ToLower().Contains((description ?? string.Empty).ToLower())
Marko
źródło
Nie możesz wygenerować wyjątku dla zapytania przetłumaczonego na SQL
Alex Zhukovskiy
@AlexZhukovskiy Jak to w ogóle ma znaczenie dla tego problemu? Jeśli fi.DESCRIPTION jest null lub description jest null, otrzymujesz wyjątek odwołania o wartości null w języku C #. Nie ma znaczenia, na co konwertuje zapytanie LINQ po stronie SQL. Oto dowód: dotnetfiddle.net/5pZ1dY
Marko
Ponieważ to zapytanie nie powiedzie się podczas tłumaczenia na SQL, ponieważ nie obsługuje operatora łączenia wartości null. I prawdopodobnie odpytujesz bazę danych zamiast ładować wszystkie wpisy, aby użyć łączenia zerowego po stronie klienta. Więc jeśli go używasz - jest w porządku po stronie klienta, ale kończy się niepowodzeniem w DB, w przeciwnym razie jesteś w porządku z DB i nie przejmujesz się nullref po stronie klienta, ponieważ tak się nie stanie, ponieważ C # nie wykonuje tego zapytania i nie robi faktycznie nie czyta obiektów pustych.
Alex Zhukovskiy
Ta odpowiedź pomogła mi rozwiązać problem, który otrzymałem na LINQ to Entities, w których robiłem .IndexOf i .Contains na IEnumerable, gdzie wartość ciągu pochodząca z bazy danych była równa null. Błąd nie pojawia się, dopóki wynik nie został wyliczony, a następnie otrzymałem komunikat o błędzie „Odwołanie do obiektu nie jest ustawione na wystąpienie obiektu”. Nie mogłem zrozumieć, dlaczego tak się dzieje, dopóki nie zobaczyłem tego postu. Dzięki!
randyh22
7

Używając C # 6.0 (który umożliwia funkcje z wyrażeniami i propagację wartości null), dla LINQ to Objects, można to zrobić w jednym wierszu, takim jak ten (również sprawdzanie wartości null):

public static bool ContainsInsensitive(this string str, string value) => str?.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0;
Alexei
źródło
To nie działa, ponieważ ContainsInsensitive nie jest poleceniem przechowywania
Sven
@Sven - tak, działa tylko dla LINQ to Objects. Poprawiłem odpowiedź. Dzięki.
Alexei
4

IndexOf działa najlepiej w tym przypadku

return this
   .ObjectContext
   .FACILITY_ITEM
   .Where(fi => fi.DESCRIPTION.IndexOf(description, StringComparison.OrdinalIgnoreCase)>=0);
Menelaos Vergis
źródło
3

Możesz użyć string.Compare

    lst.Where(x => string.Compare(x,"valueToCompare",StringComparison.InvariantCultureIgnoreCase)==0);

jeśli chcesz tylko sprawdzić zawartość, użyj opcji „Dowolny”

  lst.Any(x => string.Compare(x,"valueToCompare",StringComparison.InvariantCultureIgnoreCase)==0)
Code First
źródło
To nie odpowiada na pytanie. OP pyta o „Zawiera” w ciągu (tj. Jeden ciąg zawiera inny), a nie o to, czy zbiór ciągów zawiera pojedynczy ciąg.
andrewf
1
public static bool Contains(this string input, string findMe, StringComparison comparisonType)
{
    return String.IsNullOrWhiteSpace(input) ? false : input.IndexOf(findMe, comparisonType) > -1;
}
E Rolnicki
źródło
2
czy możemy używać niestandardowych metod rozszerzeń w zapytaniach linq? Jesteś pewny ?
Vishal Sharma
0

Szczerze mówiąc, to nie musi być trudne. Może się wydawać, że na początku, ale tak nie jest. Oto prosta kwerenda linq w C #, która działa dokładnie zgodnie z żądaniem.

W moim przykładzie pracuję przeciwko liście osób, które mają jedną właściwość o nazwie FirstName.

var results = ClientsRepository().Where(c => c.FirstName.ToLower().Contains(searchText.ToLower())).ToList();

Spowoduje to przeszukanie bazy danych przy użyciu wyszukiwania małymi literami, ale zwróci pełne wyniki.

Frank Thomas
źródło
-2

Użyj metody String.Equals

public IQueryable<FACILITY_ITEM> GetFacilityItemRootByDescription(string description)
{
    return this.ObjectContext.FACILITY_ITEM
           .Where(fi => fi.DESCRIPTION
           .Equals(description, StringComparison.OrdinalIgnoreCase));
}
Francesco Moroni
źródło