Почему LINQ to Entities string.Contains (string.Empty) соответствует чему-либо, кроме string.Contains (string.Empty.ToLower ()) ничего не соответствует? - PullRequest
1 голос
/ 15 мая 2019

Я пишу запрос для службы репозитория для таблицы инвентаризации. К вашему сведению: мы используем C # 7.0, EF 6, и мы используем Moq для тестирования наших запросов.

Я узнал, что когда string.Contains(...), который по умолчанию чувствителен к регистру, помещается в запрос LINQ, а затем конвертируется в SQL, в результате получается регистр в (найдены другие сообщения SO, чтобы помочь с это, и мы будем иметь дело с этим), и я также обнаружил, что функции string.Contains(...), кажется, имеют причуду, когда аргумент string.Empty и преобразуется в нижний регистр (найдено нет SO сообщения о это).

Попытки использовать строку без учета регистра. Перегрузки Contains (...) отбрасываются с исключением, когда LINQ to Entities пытается преобразовать в SQL, поэтому я должен вручную указать column.Contains(argument.ToLower()), чтобы оба SQL-запрос LINQ to Entities для работы по назначению и для смоделированного модульного теста на нечувствительность к регистру.

Проблема: Если аргумент является строкой. Пусто, ничего не найдено. Преступник - это когда аргумент преобразуется в нижний регистр.

Это не препятствие (простое перемещение проверки argument.ToLower() за пределы запроса решило проблему, и в любом случае это было бы немного более эффективно), но я все еще хочу знать, в чем дело.

public List<InventoryModel> FindByTrackingNumberSubstring( string substring )
{
    // (bad) matches nothing when argument is string.Empty
    //var query = _modelTable.Where( entity => entity.Tracking_Number.ToLower().Contains( substring.ToLower() ) );

    // (good) matches everything when argument is string.Empty
    string lower = substring.ToLower();
    var query = _modelTable.Where( entity => entity.Tracking_Number.ToLower().Contains( lower ) );

    return query.ToList<InventoryModel>();
}

// SQL for queries 1 and 2, respectively (stripped out SELECT and FROM stuff for brevity)
WHERE ((CASE WHEN (( CAST(CHARINDEX(LOWER(@p__linq__0), LOWER([Extent1].[Tracking Number])) AS int)) > 0) THEN cast(1 as bit) WHEN ( NOT (( CAST(CHARINDEX(LOWER(@p__linq__0), LOWER([Extent1].[Tracking Number])) AS int)) > 0)) THEN cast(0 as bit) END) = 1)
WHERE ((CASE WHEN (LOWER([Extent1].[Tracking Number]) LIKE @p__linq__0 ESCAPE N'~') THEN cast(1 as bit) WHEN ( NOT (LOWER([Extent1].[Tracking Number]) LIKE @p__linq__0 ESCAPE N'~')) THEN cast(0 as bit) END) = 1)

Я провел некоторую проверку и обнаружил, что в SQL-запросе LINQ to Entities это string.Contains(string.Empty) соответствует чему угодно, и я обнаружил, что string.Empty.ToLower() == string.Empty соответствует чему угодно, но соедините их вместе, а C # и LINQ to Entities расходятся. В первом случае string.Contains(string.Empty.ToLower()) соответствует чему-либо (как и ожидалось), но во втором ничего не соответствует ..

Почему?

1 Ответ

2 голосов
/ 16 мая 2019

Я полагаю, что это будет причуда поставщика SQL Server для EF, поскольку при выполнении .ToLower() для критериев и сравниваемого поля он распознает запрос как явно нечувствительный к регистру и заменяет запрос LIKE насравнение CHARINDEX, которое не обрабатывает пустые строки в SQL Server таким же образом.Поведение для чувствительности к регистру будет зависеть от механизма базы данных, а в случае SQL Server - сопоставления, выбранного для строк в базе данных.Не уверен, почему LOWER (Tracking_Number) LIKE LOWER ('%%') не мог быть использован.

Лично, при составлении выражений EF Linq, мой код запроса всегда проверяет IsNullOrEmpty в строках и не добавляется.Where() условия, когда фактические критерии не были предоставлены.Таким образом, предложения WHERE применяются только для предоставленных критериев.

Т.е. если я верю, что БД не будет сопоставляться с учетом регистра:

if(!string.IsNullOrEmpty(substring))
    query = query.Where(entity => entity.Tracking_Number.Contains(substring));

, если я обеспокоен тем, что база данных может бытьс учетом регистра:

if(!string.IsNullOrEmpty(substring))
    query = query.Where( entity => entity.Tracking_Number.ToLower().Contains(substring.ToLower()));

Даже там я бы предпочел установить стандарт, чтобы Tracking_Number всегда сохранялся как значение в нижнем регистре, если база данных обслуживает исключительно это приложение.Свойства сущности обеспечили бы, чтобы любое заданное значение было в нижнем регистре.(устранение необходимости в .Tracking_Number.ToLower () в запросах.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...