简体   繁体   English

Linq&String.ToLower()奇怪的行为

[英]Linq & String.ToLower() strange behavior


I have a query on the server side that returns a list of distinct cities from a zipcode table. 我在服务器端有一个查询,它从一个zipcode表返回一个不同城市的列表。
I'm using WCF RIA Service. 我正在使用WCF RIA服务。
The following query successfully returns 228 cities when provincename == "" provincename == ""时,以下查询成功返回228个城市

    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 });
    }

but if I use ToLower() method as below, the query returns 0 cities when provincename == "" . 但如果我使用如下的ToLower()方法,则当provincename == ""时,查询返回0个城市。

    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 });
    }

Why isn't the query returning anything? 为什么查询不返回任何内容?

Try checking the SQL generated, either by using DB management tools, or calling .ToTraceString() at the end of the query expression. 尝试使用数据库管理工具检查生成的SQL,或者在查询表达式的末尾调用.ToTraceString()。

Reference: http://blog.aggregatedintelligence.com/2010/06/viewing-entity-framework-generated-sql.html 参考: http//blog.aggregatedintelligence.com/2010/06/viewing-entity-framework-generated-sql.html

We use ToTraceString at work using an extension: 我们在使用扩展时使用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;
}

It can then be used as follows: 然后可以使用如下:

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();
}

Please forgive me for any typos, this is from memory. 请原谅我的任何错别字,这是来自记忆。 Hopefully it will help you understand your problem. 希望它能帮助您理解您的问题。

The Explanation 说明

I was having the same problem and I found out why this is occurring. 我遇到了同样的问题,我发现了为什么会这样。 Running SQL Profiler I saw that the WHERE statements generated from LINQ to SQL are very different in each case. 运行SQL事件探查器我发现从LINQ到SQL生成的WHERE语句在每种情况下都是非常不同的。

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

would render in SQL as: 将在SQL中呈现为:

WHERE [Province] LIKE N'%%'

As you have experienced, LIKE '%%' would match any non null results. 正如您所经历的那样, LIKE '%%'将匹配任何非null结果。

.
However, 然而,

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

would render in SQL as: 将在SQL中呈现为:

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

This is very different than LIKE '%%' . 这与LIKE '%%'非常不同。 SQL is essentially looking to see what character string.Empty is in the string Province . SQL基本上是在查看字符串省中的字符串string.Empty The result of the CHARINDEX on an empty string is 0 which is why there are no results being returned. CHARINDEX在空字符串上的结果为0,这就是没有返回结果的原因。

.

The Workaround 解决方法

This is a little hackish but it'll work. 这有点hackish但它会起作用。 Only call .ToLower() if the the string is not empty. 如果字符串不为空,则仅调用.ToLower() The following code is an example of something that should work for you. 以下代码是适合您的一些示例。

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 });
}

By structuring your code like this, LINQ to SQL will render as LIKE '%%' if provinceName is an empty string, otherwise it'll render as CHARINDEX . 通过像这样构造你的代码,如果provinceName是一个空字符串, LINQ to SQL将呈现为LIKE '%%' ,否则它将呈现为CHARINDEX It also helps if there is a null passed in. 如果传入null,它也会有所帮助。

This worked for me, give it a try if you like it 这对我有用,如果你喜欢,试一试

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

Note: I am querying it against Oracle 注意:我正在查询Oracle

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM