4 votes

Comportement étrange de Linq & String.ToLower()

J'ai une requête côté serveur qui renvoie une liste de villes distinctes à partir d'un tableau de codes postaux.
J'utilise le service WCF RIA.
La requête suivante renvoie avec succès 228 villes lorsque 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 });
    }

mais si j'utilise la méthode ToLower() comme ci-dessous, la requête renvoie 0 villes quand 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 });
    }

Pourquoi la requête ne renvoie rien ?

7voto

Will Points 1776

Essayez de vérifier le SQL généré, soit en utilisant des outils de gestion de base de données, soit en appelant .ToTraceString() à la fin de l'expression de la requête.

Référence : http://blog.aggregatedintelligence.com/2010/06/viewing-entity-framework-generated-sql.html

Nous utilisons ToTraceString au travail en utilisant une extension :

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

Il peut alors être utilisé comme suit :

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

Veuillez me pardonner pour toute erreur de frappe, ceci est de mémoire. J'espère que cela vous aidera à comprendre votre problème.

6voto

Billy McKee Points 193

L'explication

J'avais le même problème et j'ai trouvé la raison pour laquelle cela se produit. En exécutant SQL Profiler, j'ai vu que le générées à partir de LINQ to SQL sont très différentes dans chaque cas.

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

serait rendu en SQL comme :

WHERE [Province] LIKE N'%%'

Comme vous l'avez expérimenté, LIKE '%%' correspondrait à tout non nul les résultats.

.
Cependant,

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

serait rendu en SQL comme :

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

C'est très différent de LIKE '%%' . SQL cherche essentiellement à voir quel caractère string.Empty est dans la chaîne Province . Le résultat de la CHARINDEX sur une chaîne vide est de 0, ce qui explique pourquoi aucun résultat n'est renvoyé.

.

La solution de rechange

C'est un peu bricolé mais ça va marcher. Appelez seulement .ToLower() si la chaîne n'est pas vide. Le code suivant est un exemple de quelque chose qui devrait fonctionner pour vous.

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

En structurant votre code comme ceci, LINQ to SQL sera rendu comme LIKE '%%' si Nom de la province est une chaîne vide, sinon elle sera rendue comme suit CHARINDEX . Il est également utile qu'une valeur nulle soit transmise.

0voto

saquib adil Points 118

Cela a fonctionné pour moi, essayez-le si vous l'aimez.

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

Note : Je fais une requête contre Oracle.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X