LINQ Містить чутливий до випадків випадків


174

Цей код враховує регістри, як зробити його нечутливим?

public IQueryable<FACILITY_ITEM> GetFacilityItemRootByDescription(string description)
{
    return this.ObjectContext.FACILITY_ITEM.Where(fi => fi.DESCRIPTION.Contains(description));
}

Відповіді Шьорда правильні, але ... Я хочу отримати результати пошуку імен з турецькою İ (наприклад) під час написання i і навпаки. У цьому випадку здається, що ToLower є правильним шляхом. Будь ласка, виправте мене, якщо я помиляюся. Про турецьку İ: en.wikipedia.org/wiki/Dotted_and_dotless_I
Він Нрік

@HeNrik - Як обговорювалося в посиланнях для тестування в Туреччині в коментарі JYelton під прийнятою відповіддю, якщо працювати з турецькою культурою, ці два я будуть різними - тому ви не знайдете імен з іншими i. Ви хочете ToLowerInvariant. Дивіться дискусію під різними відповідями тут .
ToolmakerSteve

це старе питання, але варто зазначити, що в поточній версії EF core 2.0 ToLower () працює таким чином person.Where (p => p.Name.ToLower (). Містить (myParam.Name.ToLower () )); Я використовую це у запиті Linq проти БД Postgres. У мене немає нечутливості випадків щодо порівняння стовпців у БД, і я перевірив, що без ToLower () збіг чітко враховує регістри.
shelbypereira

Відповіді:


72

Припустимо, що ми тут працюємо зі струнами, ось ще одне "елегантне" рішення IndexOf().

public IQueryable<FACILITY_ITEM> GetFacilityItemRootByDescription(string description)
{
    return this.ObjectContext.FACILITY_ITEM
        .Where(fi => fi.DESCRIPTION
                       .IndexOf(description, StringComparison.OrdinalIgnoreCase) != -1);
}

7
Приємно. Для моїх власних цілей це не працює для LINQ для Суб'єктів. Хоча гарне рішення для LINQ для об'єктів.
Даміан Пауелл

242
fi => fi.DESCRIPTION.ToLower().Contains(description.ToLower())

49
Як прокоментував Джон Скіт відповідне запитання , цей метод не пройде тест на Туреччину .
JYelton

5
Ні, але бази даних працюють з наборами символів і зіставленням. Якщо ви намагаєтеся перенести роботу з базою даних, вам доведеться зробити деякі припущення щодо набору символів і порівняння, правда?
Крістофер Стівенсон

66
Місти повинні використовувати IEqualityComparer<string>атрибут для обробки того, як буде працювати порівняння. Використовуйте ToLower і ToUpper для перевірки рівності - це погана ідея. Спробуйте: .Contains(description, StringComparer.CurrentCultureIgnoreCase)наприклад
Dorival

19
Коментар від @Dorival не працює, оскільки він дає це повідомлення про помилки: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
Containsз StringComparerне отримує рядок як параметр, тому це буде помилка збирання. IndexOfна , Queryableймовірно , не може бути переведена на SQL. Особисто я вважав цю відповідь цілком справедливою, коли ми говоримо про LINQ до бази даних.
Thariq Nugrohotomo

122

Якщо запит LINQ виконується в контексті бази даних, виклик до Contains()цього картографується LIKEоператору:

.Where(a => a.Field.Contains("hello")) стає Field LIKE '%hello%'. LIKEОператор чутливий до регістру за замовчуванням, але може бути змінений шляхом зміни параметрів сортування стовпця .

Якщо запит LINQ виконується в контексті .NET, ви можете використовувати IndexOf () , але цей метод не підтримується в LINQ в SQL.

LINQ до SQL не підтримує методи, які приймають CultureInfo як параметр, ймовірно, тому, що він не може гарантувати, що сервер SQL обробляє культури так само, як .NET. Це не зовсім вірно, тому що це підтримує StartsWith(string, StringComparison).

Однак, схоже, він не підтримує метод, який оцінюється LIKEв LINQ до SQL, і порівняння нечутливого до регістру в .NET, що робить неможливим послідовно робити нечутливі до регістру Contains ().


Просто FYI EF 4.3 не підтримує StartsWith. Я отримую: LINQ до Entities не розпізнає метод 'Boolean StartsWith (System.String, System.StringComp poredation)'
nakhli

StartWith перетворюється на LIKE "привіт%"?
Барт Калікто

Посилання clickdata мертве.
Адам Паркін

2
великі зусилля для
пошуку коду

1
Отже, які є варіанти при використанні EF, в одному контексті мені потрібно зробити insensitiveпошук випадків , а в іншому мені це потрібно case sensitive. Мені просто потрібно виконати ступінь продуктивності та використовувати "toLower ()"?
Zapnologica

12

Тут прийнята відповідь не згадує факту, що якщо у вас є нульовий рядок, ToLower () викине виняток. Більш безпечним способом було б зробити:

fi => (fi.DESCRIPTION ?? string.Empty).ToLower().Contains((description ?? string.Empty).ToLower())

Ви не можете покласти виняток у запиті, перекладеному на SQL
Алекс Жуковський

@AlexZhukovskiy Як це навіть стосується цієї проблеми? Якщо fi.DESCRIPTION є null або опис є null, ви отримуєте C # null посилання виключення. Не має значення, у що LINQ запит перетворюється на стороні SQL. Ось доказ: dotnetfiddle.net/5pZ1dY
Marko

Оскільки цей запит не вдасться перевести в SQL, оскільки він не підтримує оператор зчитування нуля. І ви, ймовірно, запитуєте базу даних замість того, щоб завантажувати всі записи, щоб використовувати нульове з’єднання на стороні клієнта. Отже, якщо ви його використовуєте - це добре на стороні клієнта, але не в роботі з БД, інакше ви все в порядку з БД, і ви не переймаєтесь nullref на стороні клієнта, оскільки цього не станеться, оскільки C # не виконує цей запит і не насправді не читають нульові об’єкти.
Олексій Жуковський

Ця відповідь допомогла мені вирішити проблему, з якою я потрапляв на LINQ до об'єктів, де я робив .IndexOf та .Contains на IEnumerable, де значення рядка, що надходить із бази даних, було нульовим. Я не отримував помилку, поки результат не був перерахований, і тоді я отримав повідомлення про помилку, що "Посилання на об'єкт не встановлено на екземпляр об'єкта". Я не міг зрозуміти, чому це відбувається, поки не побачив цю публікацію. Дякую!
randyh22

7

Використовуючи C # 6.0 (що дозволяє виконувати функції експресії та нульове поширення), для LINQ до об'єктів це можна зробити в одному рядку, як це (також перевіряючи на null):

public static bool ContainsInsensitive(this string str, string value) => str?.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0;

Це не працює, тому що ContainsInsensitive не є командою магазину
Sven

@Sven - так, він працює лише для LINQ для об'єктів. Я виправив свою відповідь. Дякую.
Олексій

4

IndexOf найкраще працює в цьому випадку

return this
   .ObjectContext
   .FACILITY_ITEM
   .Where(fi => fi.DESCRIPTION.IndexOf(description, StringComparison.OrdinalIgnoreCase)>=0);

3

Ви можете використовувати string.Compare

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

якщо ви просто хочете перевірити, чи містить, тоді використовуйте "Будь-який"

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

Це не відповідає на запитання. ОП запитує про "Містить" всередині рядка (тобто, одна рядок містить іншу), а не те, чи містить колекція рядків одну строку.
andrewf

1
public static bool Contains(this string input, string findMe, StringComparison comparisonType)
{
    return String.IsNullOrWhiteSpace(input) ? false : input.IndexOf(findMe, comparisonType) > -1;
}

2
чи можемо ми використовувати спеціальні методи розширення у запитах linq? ти впевнений ?
Вішал Шарма


0

Чесно кажучи, це не повинно бути складним. Може здатися, що на початку, але це не так. Ось простий запит linq в C #, який виконує саме так, як вимагали.

У своєму прикладі я працюю проти списку осіб, які мають одне властивість під назвою FirstName.

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

Це дозволить здійснити пошук у базі даних за допомогою малих записів, але поверне повні результати.


-2

Використовуйте метод String.Equals

public IQueryable<FACILITY_ITEM> GetFacilityItemRootByDescription(string description)
{
    return this.ObjectContext.FACILITY_ITEM
           .Where(fi => fi.DESCRIPTION
           .Equals(description, StringComparison.OrdinalIgnoreCase));
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.