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 - Как уже говорилось в «Тестовой ссылке Турции» в комментарии Джелтона под принятым ответом, при работе с турецкой культурой эти два «я» будут отличаться - поэтому вы не найдете имен с другим «я». Вы хотите ToLowerInvariant. Смотрите обсуждение под различными ответами здесь .
ToolmakerSteve

это старый вопрос, но стоит отметить, что в текущей версии EF core 2.0 ToLower () работает следующим образом: person.Where (p => p.Name.ToLower (). Contains (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 to Entities. Хорошее решение для LINQ to Objects, хотя.
Дамиан Пауэлл

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

49
Как прокомментировал связанный вопрос Джон Скит , этот метод не пройдет тест Турции .
Джелтон

5
Нет, но базы данных работают на основе наборов символов и параметров сортировки. Если вы пытаетесь перенести работу в базу данных, вы должны сделать некоторые предположения о наборе символов и сопоставлении, верно?
Кристофер Стивенсон

66
Contains должен использовать 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
ошибке

6
Containswith StringComparerне получает строку в качестве параметра, поэтому это будет ошибка сборки. IndexOfна, Queryableвероятно, не может быть переведен в SQL. Лично я нашел этот ответ полностью действительным, когда мы говорим о LINQ to database.
Тарик Нугрохотомо

122

Если запрос LINQ выполняется в контексте базы данных, вызов Contains()сопоставляется LIKEоператору:

.Where(a => a.Field.Contains("hello")) становится Field LIKE '%hello%'. По LIKEумолчанию оператор нечувствителен к регистру, но это можно изменить, изменив параметры сортировки столбца. .

Если запрос LINQ выполняется в контексте .NET, вы можете использовать IndexOf () , но этот метод не поддерживается в LINQ to SQL.

LINQ to SQL не поддерживает методы, которые принимают CultureInfo в качестве параметра, возможно потому, что он не может гарантировать, что SQL-сервер обрабатывает культуры так же, как .NET. Это не совсем верно, потому что это поддерживает StartsWith(string, StringComparison).

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


Просто FYI EF 4.3 не поддерживает StartsWith. Я получаю: LINQ to Entities не распознает метод 'Boolean StartsWith (System.String, System.StringComparison)'
nakhli

StartWith конвертирует в LIKE 'привет%'?
Барт Каликсто

Ссылка по кликдате не работает.
Адам Паркин

2
большие усилия по
изучению сгенерированного

1
Итак, какие есть варианты при использовании EF, В одном контексте мне нужно выполнить insensitiveпоиск по кейсу, а в другом мне это нужно case sensitive. Должен ли я просто взять производительность и использовать toLower ()?
Запнологика

12

В принятом ответе здесь не упоминается тот факт, что при наличии пустой строки ToLower () сгенерирует исключение. Более безопасный способ был бы сделать:

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

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

@AlexZhukovskiy Какое отношение это имеет к этой проблеме? Если fi.DESCRIPTION имеет значение null или description имеет значение null, вы получаете исключительную ссылку C # null. Не имеет значения, во что конвертируется запрос LINQ на стороне SQL. Вот доказательство: dotnetfiddle.net/5pZ1dY
Марко

Потому что этот запрос не будет преобразован в SQL, потому что он не поддерживает пустой оператор объединения. И вы, вероятно, запрашиваете базу данных вместо того, чтобы загружать все записи, чтобы использовать нулевое объединение на стороне клиента. Так что, если вы используете его - это нормально на стороне клиента, но не работает на БД, в противном случае вы в порядке с БД и вас не волнует nullref на стороне клиента, потому что это не произойдет, потому что C # не выполняет этот запрос и не выполняет на самом деле не читать нулевые объекты.
Алексей Жуковский,

Этот ответ помог мне решить проблему, с которой я столкнулся в LINQ to Entities, где я выполнял .IndexOf и .Contains в IEnumerable, где строковое значение, поступающее из базы данных, было нулевым. Я не получал сообщение об ошибке, пока результат не был перечислен, а затем я получил сообщение об ошибке «Ссылка на объект не установлена ​​для экземпляра объекта». Я не мог понять, почему это происходит, пока я не увидел этот пост. Спасибо!
randyh22

7

Используя C # 6.0 (который допускает функции выражения выражений и распространение нуля), для LINQ to Objects это можно сделать в одной строке, например, так (также проверяя наличие нуля):

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

Это не работает, потому что ContainsInsensitive не является командой хранилища
Sven

@Sven - да, это работает только для LINQ to Objects. Я исправил свой ответ. Спасибо.
Алексей

4

IndexOf работает лучше всего в этом случае

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

3

Вы можете использовать строку.

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

если вы просто хотите проверить содержимое, используйте «Любой»

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

Это не отвечает на вопрос. ОП спрашивает о «Содержит» внутри строки (то есть, одна строка содержит другую), а не содержит ли набор строк одну строку.
Андрей

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.