138 votes

LINQ to Entities ne reconnaît pas la méthode 'System.String ToString()', et cette méthode ne peut pas être traduite en une expression de magasin.

Je suis en train de migrer quelques trucs d'un serveur mysql vers un serveur sql mais je n'arrive pas à trouver comment faire fonctionner ce code :

using (var context = new Context())
{
    ...

    foreach (var item in collection)
    {
        IQueryable<entity> pages = from p in context.pages
                                   where  p.Serial == item.Key.ToString()
                                   select p;
        foreach (var page in pages)
        {
            DataManager.AddPageToDocument(page, item.Value);
        }
    }

    Console.WriteLine("Done!");
    Console.Read();
}

Quand il entre dans la deuxième foreach (var page in pages) il jette une exception en disant :

LINQ to Entities ne reconnaît pas la méthode 'System.String ToString()', et cette méthode ne peut pas être traduite en une expression de magasin. expression.

Quelqu'un sait-il pourquoi cela se produit ?

140voto

Josh Points 23923

Il suffit de sauvegarder la chaîne dans une variable temporaire et de l'utiliser dans votre expression :

var strItem = item.Key.ToString();

IQueryable<entity> pages = from p in context.pages
                           where  p.Serial == strItem
                           select p;

Le problème se pose parce que ToString() n'est pas vraiment exécutée, elle est transformée en un Groupe de méthodes et ensuite analysé et traduit en SQL. Comme il n'y a pas de ToString() équivalent, l'expression échoue.

Nota:

N'oubliez pas de consulter également Réponse d'Alex concernant le SqlFunctions qui a été ajoutée ultérieurement. Dans de nombreux cas, elle peut éliminer le besoin de la variable temporaire.

17 votes

Que se passe-t-il si mon ToString() est appliqué sur le côté gauche de l'égalité ? Par exemple, p.Serial.ToString() = item.

3 votes

@dotNet Cela échouera toujours parce que l'ensemble est transformé en une expression, qu'Entity Framework essaie de transformer en SQL valide. Il y a quelques méthodes qu'il sait gérer, mais ToString() n'en fait pas partie.

7 votes

@Josh : Je comprends que ça va échouer. Ce que je demandais est une solution de ce scénario, parce que la solution ci-dessus ne peut évidemment pas être appliquée là.

77voto

Alex Points 149

Comme d'autres l'ont répondu, cela se produit parce que le fichier .ToString ne parvient pas à être converti en SQL pertinent lors de son passage dans la base de données.

Cependant, Microsoft fournit le Classe SqlFunctions qui est une collection de méthodes qui peuvent être utilisées dans des situations comme celle-ci.

Dans ce cas, ce que vous recherchez ici est SqlFunctions.StringConvert :

from p in context.pages
where  p.Serial == SqlFunctions.StringConvert((double)item.Key.Id)
select p;

C'est une bonne chose lorsque la solution avec des variables temporaires n'est pas souhaitable pour une raison quelconque.

Comme pour les SqlFunctions, vous disposez également de la fonction EntityFunctions (l'EF6 étant supprimé par DbFunctions ) qui fournit un ensemble différent de fonctions qui sont également agnostiques par rapport à la source de données (non limitées à SQL, par exemple).

4 votes

Ils ont ajouté la classe SqlFunctions en .NET 4 et je viens juste de l'apprendre ? Excellente découverte.

25voto

Justin Niessner Points 144953

Le problème est que vous appelez ToString dans une requête LINQ to Entities. Cela signifie que l'analyseur syntaxique essaie de convertir l'appel ToString en son équivalent SQL (ce qui n'est pas possible... d'où l'exception).

Il suffit de déplacer l'appel à ToString sur une ligne distincte :

var keyString = item.Key.ToString();

var pages = from p in context.entities
            where p.Serial == keyString
            select p;

12voto

cynicaldoctor Points 203

J'ai eu un problème similaire. Je l'ai résolu en appelant ToList() sur la collection d'entités et en interrogeant la liste. Si la collection est petite, c'est une option.

IQueryable<entity> pages = context.pages.ToList().Where(p=>p.serial == item.Key.ToString())

J'espère que cela vous aidera.

50 votes

Veuillez noter que cela permettra de récupérer todo Afficher les entités de la base de données, et faire le filtrage du côté client au lieu de la base de données généralement pas une bonne chose.

3 votes

Il est vrai que cette méthode serait inefficace pour toute table contenant plus d'un enregistrement, c'est-à-dire toutes les tables existantes :-). Cependant, cette réponse m'a aidé aujourd'hui parce que je faisais une projection .Select qui incluait toString(), donc appeler .ToList() avant n'a pas eu de pénalité de performance pour moi et appeler .ToList() m'a permis d'utiliser le formatage .ToString() et mon instruction .Select...

6voto

Daniel Hilgarth Points 90722

Changez-le comme ceci et ça devrait fonctionner :

var key = item.Key.ToString();
IQueryable<entity> pages = from p in context.pages
                           where  p.Serial == key
                           select p;

La raison pour laquelle l'exception n'est pas levée dans la ligne où la requête LINQ est déclarée, mais dans la ligne de la requête LINQ. foreach est la fonction d'exécution différée, c'est-à-dire que la requête LINQ n'est pas exécutée tant que vous n'essayez pas d'accéder au résultat. Et cela se passe dans le foreach et pas plus tôt.

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