web-dev-qa-db-fra.com

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 migre certaines choses d'un serveur mysql vers un serveur SQL mais je n'arrive pas à comprendre 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();
}

Lorsqu'il entre dans la deuxième foreach (var page in pages), il lève une exception disant:

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

Quelqu'un sait pourquoi cela se produit?

119
Erre Efe

Sauvegardez simplement la chaîne dans une variable temporaire, puis utilisez-la 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 car ToString() n'est pas vraiment exécuté, il est transformé en MethodGroup puis analysé et traduit en SQL. Comme il n'y a pas d'équivalent ToString(), l'expression échoue.

Remarque:

Assurez-vous également de vérifier la réponse d'Alex concernant la classe d'assistance SqlFunctions qui a été ajoutée plus tard. Dans de nombreux cas, cela peut éliminer le besoin de la variable temporaire.

130
Josh

Comme d'autres l'ont répondu, cela se produit, car .ToString ne parvient pas à traduire en SQL pertinent lors de son insertion dans la base de données.

Cependant, Microsoft fournit la classe SqlFunctions , qui est un ensemble de méthodes pouvant être utilisées dans des situations comme celle-ci. 

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

Bon lorsque la solution avec des variables temporaires n'est pas souhaitable pour quelque raison que ce soit.

Semblable à SqlFunctions, vous disposez également de EntityFunctions (avec EF6 obsolète de DbFunctions ), qui fournit un ensemble de fonctions différent qui sont également indépendantes de la source de données (par exemple SQL).

64
Alex

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

Tout ce que vous avez à faire est 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;
23
Justin Niessner

Avait un problème similaire . 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 t'aides.

10
cynicaldoctor

Changez-le comme ça et ça devrait marcher:

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 à laquelle la requête LINQ est déclarée mais dans la ligne à foreach est la fonctionnalité d'exécution différée, c'est-à-dire que la requête LINQ n'est pas exécutée tant que vous n'avez pas essayé d'accéder au résultat. Et cela se produit dans la variable foreach et pas avant.

6
Daniel Hilgarth

Transformez la table en Enumerable, puis vous appelez les méthodes LINQ en utilisant la méthode ToString() dans:

    var example = contex.table_name.AsEnumerable()
.Select(x => new {Date = x.date.ToString("M/d/yyyy")...)

Mais faites attention lorsque vous appelez des méthodes AsEnumerable ou ToList car vous demanderez toutes les données de toutes les entités avant cette méthode. Dans mon cas ci-dessus, j'ai lu toutes les lignes table_name en une requête. 

3
neustart47

Dans MVC, supposez que vous recherchez un ou plusieurs enregistrements en fonction de vos besoins ou de vos informations. Il fonctionne correctement.

[HttpPost]
[ActionName("Index")]
public ActionResult SearchRecord(FormCollection formcollection)
{       
    EmployeeContext employeeContext = new EmployeeContext();

    string searchby=formcollection["SearchBy"];
    string value=formcollection["Value"];

    if (formcollection["SearchBy"] == "Gender")
    {
        List<MvcApplication1.Models.Employee> emplist = employeeContext.Employees.Where(x => x.Gender == value).ToList();
        return View("Index", emplist);
    }
    else
    {
        List<MvcApplication1.Models.Employee> emplist = employeeContext.Employees.Where(x => x.Name == value).ToList();
        return View("Index", emplist);
    }         
}
1
shakti

La mise à niveau vers Entity Framework version 6.2.0 a fonctionné pour moi.

J'étais précédemment sur la version 6.0.0.

J'espère que cela t'aides,

1
93Ramadan

J'ai eu la même erreur dans ce cas:

var result = Db.SystemLog
.Where(log =>
    eventTypeValues.Contains(log.EventType)
    && (
        search.Contains(log.Id.ToString())
        || log.Message.Contains(search)
        || log.PayLoad.Contains(search)
        || log.Timestamp.ToString(CultureInfo.CurrentUICulture).Contains(search)
    )
)
.OrderByDescending(log => log.Id)
.Select(r => r);

Après avoir passé beaucoup trop de temps à déboguer, j'ai compris que l'erreur était apparue dans l'expression logique. 

La première ligne search.Contains(log.Id.ToString()) fonctionne correctement, mais la dernière ligne qui traite d'un objet DateTime l'a fait échouer lamentablement: 

|| log.Timestamp.ToString(CultureInfo.CurrentUICulture).Contains(search)

Supprimez la ligne problématique et le problème résolu.

Je ne comprends pas bien pourquoi, mais il semble que ToString () est une expression LINQ pour les chaînes, mais pas pour les entités. LINQ for Entities traite des requêtes de base de données telles que SQL, et SQL n'a aucune notion de ToString (). En tant que tel, nous ne pouvons pas jeter ToString () dans une clause .Where (). 

Mais comment fonctionne alors la première ligne? Au lieu de ToString (), SQL a CAST et CONVERT, donc je suppose que jusqu'à présent, linq pour les entités l'utilise dans certains cas simples. Les objets DateTime ne se révèlent pas toujours aussi simples ...

0
pekaaw

Si vous voulez vraiment taper ToString dans votre requête, vous pouvez écrire un visiteur d'arborescence d'expression qui réécrit l'appel en ToString avec un appel à la fonction StringConvert appropriée :

using System.Linq;
using System.Data.Entity.SqlServer;
using System.Linq.Expressions;
using static System.Linq.Expressions.Expression;
using System;

namespace ToStringRewriting {
    class ToStringRewriter : ExpressionVisitor {
        static MethodInfo stringConvertMethodInfo = typeof(SqlFunctions).GetMethods()
                 .Single(x => x.Name == "StringConvert" && x.GetParameters()[0].ParameterType == typeof(decimal?));

        protected override Expression VisitMethodCall(MethodCallExpression node) {
            var method = node.Method;
            if (method.Name=="ToString") {
                if (node.Object.GetType() == typeof(string)) { return node.Object; }
                node = Call(stringConvertMethodInfo, Convert(node.Object, typeof(decimal?));
            }
            return base.VisitMethodCall(node);
        }
    }
    class Person {
        string Name { get; set; }
        long SocialSecurityNumber { get; set; }
    }
    class Program {
        void Main() {
            Expression<Func<Person, Boolean>> expr = x => x.ToString().Length > 1;
            var rewriter = new ToStringRewriter();
            var finalExpression = rewriter.Visit(expr);
            var dcx = new MyDataContext();
            var query = dcx.Persons.Where(finalExpression);

        }
    }
}
0
Zev Spitz