Сравнение строк без учета регистра в LINQ-to-SQL

124

Я читал, что нецелесообразно использовать ToUpper и ToLower для выполнения сравнений строк без учета регистра, но я не вижу альтернативы, когда дело доходит до LINQ-to-SQL. Аргументы ignoreCase и CompareOptions String.Compare игнорируются LINQ-to-SQL (если вы используете чувствительную к регистру базу данных, вы получаете сравнение с учетом регистра, даже если вы запрашиваете сравнение без учета регистра). Является ToLower или ToUpper лучшим вариантом здесь? Лучше другого? Мне показалось, что я где-то читал, что ToUpper был лучше, но я не знаю, применимо ли это здесь. (Я делаю много обзоров кода, и каждый использует ToLower.)

Dim s = From row In context.Table Where String.Compare(row.Name, "test", StringComparison.InvariantCultureIgnoreCase) = 0

Это переводит на SQL-запрос, который просто сравнивает row.Name с "test" и не возвращает "Test" и "TEST" в базе данных, чувствительной к регистру.

  • 1
    Спасибо! Это действительно спасло мою задницу сегодня. Примечание: он также работает с другими расширениями LINQ, такими как LINQQuery.Contains("VaLuE", StringComparer.CurrentCultureIgnoreCase) и LINQQuery.Except(new string[]{"A VaLUE","AnOTher VaLUE"}, StringComparer.CurrentCultureIgnoreCase) . Wahoo!
  • 0
    Забавно, я только что прочитал, что ToUpper был лучше в сравнении с этим источником: msdn.microsoft.com/en-us/library/dd465121
Теги:
linq
linq-to-sql

8 ответов

98
Лучший ответ

Как вы говорите, есть некоторые важные различия между ToUpper и ToLower, и только один из них надежно точным, когда вы пытаетесь делать проверки на случай нечувствительности к регистру.

В идеале лучший способ сделать проверку нечувствительности к регистру:

String.Equals(row.Name, "test", StringComparison.OrdinalIgnoreCase)

Обратите внимание на Ordinal IgnoreCase, чтобы сделать его безопасным. Но именно то, что вы делаете, зависит от ваших целей. Но в общем случае используйте Equals для проверок равенства и Compare, когда вы сортируете, а затем выберите правильный StringComparison для задания.

Майкл Каплан (признанный авторитет по культуре и управлению персонажами, такой как этот) имеет соответствующие должности в ToUpper vs. ToLower:

Он говорит: "String.ToUpper - Использовать ToUpper, а не ToLower, и указать InvariantCulture для того, чтобы выбрать правила обгона ОС

  • 1
    Похоже, что это не относится к SQL Server: верхняя печать ('Große Straße') возвращает GROßE STRAßE
  • 1
    Кроме того, приведенный вами пример кода имеет ту же проблему, что и код, который я предоставил, поскольку он чувствителен к регистру при запуске через LINQ-to-SQL в базе данных MS SQL 2005.
Показать ещё 20 комментариев
61

Я использовал System.Data.Linq.SqlClient.SqlMethods.Like(row.Name, "test") в моем запросе.

Выполняет сравнение без учета регистра.

  • 3
    ха! уже несколько лет пользуюсь linq 2 sql, но до сих пор не видел SqlMethods, спасибо!
  • 3
    Brilliant! Можно было бы использовать более подробно, хотя. Это одно из ожидаемых применений Like? Существуют ли возможные исходные данные, которые могут привести к ложному положительному результату? Или ложноотрицательный результат? В документации по этому методу отсутствует, где документация , которая будет описывать работу Как метод?
Показать ещё 4 комментария
9

Я пробовал это с помощью выражения Lambda, и он работал.

List<MyList>.Any (x => (String.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)) && (x.Type == qbType) );

  • 13
    Это потому, что вы используете List<> , что означает, что сравнение происходит в памяти (код C #), а не IQueryable (или ObjectQuery ), который будет выполнять сравнение в базе данных .
  • 1
    Что сказал @drzaus. Этот ответ просто неверен, учитывая, что контекст - это linq2sql, а не обычный linq.
0
where row.name.StartsWith(q, true, System.Globalization.CultureInfo.CurrentCulture)
  • 1
    Каков текст SQL, в который он переводится, и что позволяет ему быть нечувствительным к регистру в среде SQL, которая иначе воспринимала бы его как чувствительный к регистру?
0

Для меня работает следующий двухступенчатый подход (VS2010, ASP.NET MVC3, SQL Server 2008, Linq to SQL):

result = entRepos.FindAllEntities()
    .Where(e => e.EntitySearchText.Contains(item));

if (caseSensitive)
{
    result = result
        .Where(e => e.EntitySearchText.IndexOf(item, System.StringComparison.CurrentCulture) >= 0);
}
0

Для выполнения чувствительных к регистру запросов Linq to Sql объявить строковые поля чувствительными к регистру, указав тип данных сервера, используя одно из следующих:

varchar(4000) COLLATE SQL_Latin1_General_CP1_CS_AS 

или

nvarchar(Max) COLLATE SQL_Latin1_General_CP1_CS_AS

Примечание: "CS в приведенных выше типах сортировки означает" чувствительность к регистру ".

Это можно ввести в поле "Тип данных сервера" при просмотре свойства с помощью Visual Studio DBML Designer.

Подробнее см. http://yourdotnetdesignteam.blogspot.com/2010/06/case-sensitive-linq-to-sql-queries.html

  • 0
    Это проблема. Обычно поле, которое я использую, чувствительно к регистру (химическая формула CO [угарный газ] отличается от Co [кобальта]). Однако в конкретной ситуации (поиск) я хочу, чтобы co соответствовал как Co, так и CO. Определение дополнительного свойства с другим «типом данных сервера» недопустимо (linq to sql допускает только одно свойство на столбец sql). Так что до сих пор нет.
  • 0
    Кроме того, при выполнении модульного тестирования этот подход вряд ли будет совместим с макетом данных. Лучше всего использовать подход linq / lambda в принятом ответе.
0

Если вы передадите строку, не учитывающую регистр в LINQ-to-SQL, она будет передана в SQL без изменений, и сравнение будет происходить в базе данных. Если вы хотите делать сравнения строк без учета регистра в базе данных, все, что вам нужно сделать, это создать выражение лямбда, которое выполняет сравнение, а поставщик LINQ-to-SQL переведет это выражение в запрос SQL с неповрежденной строкой.

Например, этот запрос LINQ:

from user in Users
where user.Email == "[email protected]"
select user

преобразуется в следующий SQL-запрос поставщиком LINQ-to-SQL:

SELECT [t0].[Email]
FROM [User] AS [t0]
WHERE [t0].[Email] = @p0
-- note that "@p0" is defined as nvarchar(11)
-- and is passed my value of "[email protected]"

Как вы можете видеть, параметр string будет сравниваться в SQL, что означает, что все должно работать так, как вы ожидали.

  • 0
    Я не понимаю, что вы говорите. 1) Сами строки не могут быть без учета регистра или без учета регистра в .NET, поэтому я не могу передать «строку без учета регистра». 2) LINQ-запрос по сути является лямбда-выражением, и именно так я передаю свои две строки, поэтому для меня это не имеет никакого смысла.
  • 3
    Я хочу выполнить сравнение CASE-INSENSITIVE для базы данных CASE-SENSITIVE.
Показать ещё 4 комментария
-2

Помните, что существует разница между тем, работает ли запрос и работает ли он эффективно! Оператор LINQ преобразуется в T-SQL, когда целью оператора является SQL Server, поэтому вам нужно подумать о T-SQL, который будет создан.

Использование String.Equals, скорее всего, (я предполагаю) возвращает все строки из SQL Server, а затем выполняет сравнение в .NET, потому что это выражение .NET, которое не может быть переведено в T-SQL.

Другими словами, использование выражения увеличивает доступ к данным и удаляет вашу способность использовать индексы. Он будет работать на небольших столах, и вы не заметите разницы. На большой таблице он может работать очень плохо.

Это одна из проблем, которая существует в LINQ; люди больше не думают о том, как выполняются заявления, которые они пишут.

В этом случае нет способа сделать то, что вы хотите, не используя выражение - даже в T-SQL. Поэтому вы не сможете сделать это более эффективно. Даже приведенный выше ответ T-SQL (с использованием переменных с сортировкой), скорее всего, приведет к игнорированию индексов, но если это большая таблица, то стоит запустить инструкцию и посмотреть план выполнения, чтобы увидеть, был ли использован индекс.

  • 2
    Это не так (это не приводит к тому, что строки возвращаются клиенту). Я использовал String.Equals, и причина, по которой он не работает, заключается в том, что он преобразуется в сравнение строк TSQL, поведение которого зависит от параметров сортировки базы данных или сервера. Я, например, считаю, что каждое написанное мной выражение LINQ to SQL будет преобразовано в TSQL. Путь к тому, что я хочу, - использовать ToUpper, чтобы заставить сгенерированный TSQL использовать UPPER. Тогда вся логика преобразования и сравнения все еще выполняется в TSQL, поэтому вы не теряете много производительности.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню