Linq & String.ToLower () странное поведение - PullRequest
4 голосов
/ 20 октября 2010


У меня есть запрос на стороне сервера, который возвращает список отдельных городов из таблицы почтовых индексов.
Я пользуюсь службой WCF RIA.
Следующий запрос успешно возвращает 228 городов, когда provincename == ""

    public IQueryable<CityPM> GetCities(string provinceName)
    {
        return this.ObjectContext.ZipCodes.Where(z => z.Province.Contains(provinceName))
                                          .GroupBy(z => z.City)
                                          .Select(g => g.FirstOrDefault())
                                          .Select(zc => new CityPM() { ID = zc.ID, Name = zc.City });
    }

но если я использую метод ToLower (), как показано ниже, запрос возвращает 0 городов, когда provincename == "".

    public IQueryable<CityPM> GetCities(string provinceName)
    {
        return this.ObjectContext.ZipCodes.Where(z => z.Province.ToLower().Contains(provinceName.ToLower()))
                                          .GroupBy(z => z.City)
                                          .Select(g => g.FirstOrDefault())
                                          .Select(zc => new CityPM() { ID = zc.ID, Name = zc.City });
    }

Почему запрос ничего не возвращает?

Ответы [ 3 ]

7 голосов
/ 20 октября 2010

Попробуйте проверить сгенерированный SQL, используя инструменты управления БД или вызвав .ToTraceString () в конце выражения запроса.

Ссылка: http://blog.aggregatedintelligence.com/2010/06/viewing-entity-framework-generated-sql.html

Мы используем ToTraceString на работе, используя расширение:

public static IQueryable<T> TraceSql<T>(this IQueryable<T> query)
{
    var sql = ((System.Data.Objects.ObjectQuery)query).ToTraceString();

    // do whatever logging of sql you want here, eg (for web)
    // (view by visiting trace.axd within your site)
    HttpContext.Current.Trace.Write("sql", sql);

    return query;
}

Затем его можно использовать следующим образом:

public IQueryable<CityPM> GetCities(string provinceName)
{
    return this.ObjectContext.ZipCodes.Where(z => z.Province.ToLower().Contains(provinceName.ToLower()))
                                      .GroupBy(z => z.City)
                                      .Select(g => g.FirstOrDefault())
                                      .Select(zc => new CityPM() { ID = zc.ID, Name = zc.City })
                                      .TraceSql();
}

Пожалуйста, прости меня за любые опечатки, это по памяти. Надеюсь, это поможет вам понять вашу проблему.

4 голосов
/ 01 октября 2013

Объяснение

У меня была такая же проблема, и я выяснил, почему это происходит. Запуск SQL Profiler Я увидел, что операторы WHERE , сгенерированные из LINQ to SQL , сильно отличаются в каждом случае.

.Where(z => z.Province.Contains(provinceName))

будет отображаться в SQL как:

WHERE [Province] LIKE N'%%'

Как вы уже убедились, LIKE '%%' будет соответствовать любым ненулевым результатам.

.
Однако

.Where(z => z.Province.ToLower().Contains(provinceName.ToLower()))

будет отображаться в SQL как:

WHERE ( CAST( CHARINDEX(LOWER(N''), LOWER([Province])) AS int)) > 0

Это очень отличается от LIKE '%%'. SQL по сути ищет, какой символ string.Empty находится в строке Province . Результат CHARINDEX в пустой строке равен 0, поэтому никаких результатов не возвращается.

.

Обходной путь

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

public IQueryable<CityPM> GetCities(string provinceName)
{
    var lowerProvinceName = String.IsNullOrEmpty(provinceName) ? string.Empty : provinceName.ToLower();

    return this.ObjectContext.ZipCodes.Where(z => z.Province.ToLower().Contains(lowerProvinceName))
                                      .GroupBy(z => z.City)
                                      .Select(g => g.FirstOrDefault())
                                      .Select(zc => new CityPM() { ID = zc.ID, Name = zc.City });
}

Структурируя ваш код следующим образом, LINQ to SQL будет отображаться как LIKE '%%', если ProvinceName - пустая строка, в противном случае он будет отображаться как CHARINDEX. Также помогает, если передан нуль.

0 голосов
/ 30 апреля 2015

Это сработало для меня, попробуйте, если вам нравится

context.MyEntities.Where (p => p.Email.ToUpper (). Equals (muser.Email.ToUpper ()));

Примечание. Я запрашиваю его у Oracle

...