web-dev-qa-db-fra.com

LINQ to Entities ne reconnaît pas la méthode 'Format System.String (System.String, System.Object, System.Object)'

J'ai cette requête linq:

private void GetReceivedInvoiceTasks(User user, List<Task> tasks)
{
    var areaIds = user.Areas.Select(x => x.AreaId).ToArray();

    var taskList = from i in _db.Invoices
                   join a in _db.Areas on i.AreaId equals a.AreaId
                   where i.Status == InvoiceStatuses.Received && areaIds.Contains(a.AreaId)
                   select new Task {
                       LinkText = string.Format(Invoice {0} has been received from {1}, i.InvoiceNumber, i.Organisation.Name),
                       Link = Views.Edit
                   };
}

Il a des problèmes cependant. J'essaie de créer des tâches. Pour chaque nouvelle tâche lorsque je règle le texte du lien sur une chaîne constante telle que "Bonjour", tout va bien. Cependant, ci-dessus, j'essaie de créer la propriété linktext en utilisant les propriétés de la facture.

Je reçois cette erreur:

base {System.SystemException} = {"La méthode LINQ to Entities ne reconnaît pas la méthode 'System.String Format (System.String, System.Object, System.Object)' et cette méthode ne peut pas être traduite en une expression de magasin." }

Quelqu'un sait pourquoi? Quelqu'un sait-il une autre façon de le faire pour que cela fonctionne?

71
AnonyMouse

Entity Framework tente d'exécuter votre projection du côté SQL, où il n'y a pas d'équivalent à string.Format . Utilisez AsEnumerable() pour forcer l'évaluation de cette pièce avec Linq to Objects.

Sur la base de sur la réponse précédente , je vous ai proposé de restructurer votre requête de la manière suivante:

int statusReceived = (int)InvoiceStatuses.Received;
var areaIds = user.Areas.Select(x=> x.AreaId).ToArray();

var taskList = (from i in _db.Invoices
               where i.Status == statusReceived && areaIds.Contains(i.AreaId)
               select i)
               .AsEnumerable()
               .Select( x => new Task()
               {
                  LinkText = string.Format("Invoice {0} has been received from {1}", x.InvoiceNumber, x.Organisation.Name),
                  Link = Views.Edit
                });

De plus, je vois que vous utilisez des entités associées dans la requête (Organisation.Name). Assurez-vous d’ajouter la variable Include appropriée à votre requête ou de matérialiser spécifiquement ces propriétés pour une utilisation ultérieure, à savoir:

var taskList = (from i in _db.Invoices
               where i.Status == statusReceived && areaIds.Contains(i.AreaId)
               select new { i.InvoiceNumber, OrganisationName = i.Organisation.Name})
               .AsEnumerable()
               .Select( x => new Task()
               {
                  LinkText = string.Format("Invoice {0} has been received from {1}", x.InvoiceNumber, x.OrganisationName),
                  Link = Views.Edit
                });
131
BrokenGlass

IQueryable dérive de IEnumerable, la principale ressemblance est que, lorsque vous faites votre requête, elle est publiée dans le moteur de base de données dans sa langue, le moment le plus opportun est celui où vous dites à C # de gérer les données sur le serveur (pas côté client) ou de dire à SQL gérer les données.

Ainsi, lorsque vous dites IEnumerable.ToString(), C # récupère la collecte de données et appelle ToString() sur l'objet . Mais lorsque vous dites IQueryable.ToString(), C # indique à SQL d'appeler ToString() sur l'objet, mais aucune méthode de ce type n'existe dans SQL.

L'inconvénient est que, lorsque vous manipulez des données en C #, l'ensemble de la collection que vous consultez doit être construit en mémoire avant que C # applique les filtres.

La méthode la plus efficace consiste à définir la requête sous la forme IQueryable avec tous les filtres que vous pouvez appliquer.

Et ensuite le construire en mémoire et rendre le formatage des données en C #. 

IQueryable<Customer> dataQuery = Customers.Where(c => c.ID < 100 && c.Zip == 12345 && c.Name == "John Doe");

 var inMemCollection = dataQuery.AsEnumerable().Select(c => new
                                                  {
                                                     c.ID
                                                     c.Name,
                                                     c.Zip,
                                                     c.DateRegisterred.ToString("dd,MMM,yyyy")
                                                   });
15
Nikolay

Bien que SQL ne sache pas quoi faire avec un string.Format, il peut effectuer une concaténation de chaînes.

Si vous exécutez le code suivant, vous devriez obtenir les données que vous recherchez. 

var taskList = from i in _db.Invoices
               join a in _db.Areas on i.AreaId equals a.AreaId
               where i.Status == InvoiceStatuses.Received && areaIds.Contains(a.AreaId)
               select new Task {
                   LinkText = "Invoice " + i.InvoiceNumber + "has been received from " + i.Organisation.Name),
                   Link = Views.Edit
               };

Une fois que vous avez exécuté la requête, le résultat devrait être légèrement plus rapide que d'utiliser AsEnumerable (du moins, c'est ce que j'ai trouvé dans mon propre code après avoir eu la même erreur d'origine que vous). Si vous faites quelque chose de plus complexe avec C #, vous devrez quand même utiliser AsEnumerable.

0
d219