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 cependant des problèmes. J'essaye de créer des tâches. Pour chaque nouvelle tâche, lorsque je règle le texte du lien sur une chaîne constante comme "Bonjour", tout va bien. Cependant ci-dessus, j'essaie de créer le lien de propriété en utilisant les propriétés de la facture.
J'obtiens cette erreur:
base {System.SystemException} = {"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 connaît-il une autre façon de faire cela pour que cela fonctionne?
linq
entity-framework
linq-to-entities
AnonyMouse
la source
la source
Réponses:
Entity Framework tente d'exécuter votre projection du côté SQL, où il n'y a pas d'équivalent à
string.Format
. UtilisezAsEnumerable()
pour forcer l'évaluation de cette pièce avec Linq aux objets.Sur la base de la réponse précédente que je vous ai donnée, je restructurerais votre requête comme ceci:
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 });
Aussi, je vois que vous utilisez des entités liées dans la requête (
Organisation.Name
), assurez-vous d'ajouter le bonInclude
à votre requête, ou matérialisez spécifiquement ces propriétés pour une utilisation ultérieure, c'est-à-dire: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 });
la source
IQueryable
dérive deIEnumerable
, la ressemblance principale est que lorsque vous effectuez votre requête, elle est publiée dans le moteur de base de données dans son langage, le moment léger est celui où vous dites à C # de gérer les données sur le serveur (pas côté client) ou de dire à SQL de gérer Les données.Donc, fondamentalement, quand vous dites
IEnumerable.ToString()
, C # obtient la collection de données et appelleToString()
l'objet. Mais quand vous dites queIQueryable.ToString()
C # dit à SQL d'appelerToString()
l'objet, mais il n'y a pas de méthode de ce type dans SQL.L'inconvénient est que lorsque vous gérez des données en C #, toute la collection que vous recherchez doit être créée en mémoire avant que C # applique les filtres.
Le moyen le plus efficace de le faire est de faire la requête comme
IQueryable
avec tous les filtres que vous pouvez appliquer.Et puis construisez-le en mémoire et effectuez 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") });
la source
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 réellement effectué la requête, cela devrait être légèrement plus rapide que l'utilisation
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 toujours l'utiliserAsEnumerable
.la source
AsEnumerable()
peut être beaucoup plus efficace. ÉvitezAsEnumerable()
etToList()
jusqu'à ce que vous vouliez vraiment mettre tous les résultats en mémoire.