2009-08-30 3 views
4

J'ai la requête LINQ suivante:LINQ to Entities: Pourquoi ne puis-je pas utiliser la méthode Split comme condition?

var aKeyword = "ACT"; 
var results = from a in db.Activities 
       where a.Keywords.Split(',').Contains(aKeyword) == true 
       select a; 

Mots-clés est un champ délimité par des virgules.

Chaque fois que je lance cette requête, je reçois l'erreur suivante:

« LINQ to Entities ne reconnaît pas la méthode « Boolean Contient [chaîne] (System.Collections.Generic.IEnumerable`1 [System.String] , System.String) 'méthode, et cette méthode ne peut pas être traduite en une expression de magasin. "

Quelle est l'alternative à ce que j'essaie de faire?

+4

S'il vous plaît, il n'y a vraiment aucune raison de comparer le résultat d'une opération booléenne avec tr ue Utilisez simplement le bool retourné. –

+0

Évidemment ... Je l'ai fait de cette façon parce que j'utilisais LINQ et il n'a pas aimé les Contains tout seul! – James

Répondre

1

En réponse à vos considérations de performance sur un grand ensemble de données:

Vous allez faire non indexé correspondant de chaîne de caractères génériques sur le client, donc oui, il y aura perte de performance.

Y a-t-il une raison pour laquelle vous avez plusieurs mots-clés dans un champ de table? Vous pouvez normaliser cela, pour avoir une table ActivityKeywords où pour chaque activité vous avez un certain nombre d'enregistrements de mots-clés.

Activités (activity_id, .../* Supprimer le champ des mots-clés * /) ---> ActivityKeywords (activity_id, keyword_id) ---> Mots-clés (keyword_id, valeur)

Check out forme non-première normale : http://en.wikipedia.org/wiki/Database_normalization

EDIT: de plus, même si vous deviez tenir à une colonne, il y a une façon de faire tout Serverside (si vous avez une syntaxe stricte: « keyword1, keyword2, ..., keywordN »):

var aKeyword = "ACT"; 
var results = (from a in db.Activities 
       where a.Keywords.Contains("," + aKeyword) || a.Keywords.Contains(aKeyword + ",") 
       select a; 
+0

C'était vraiment juste pour la facilité d'utilisation. La plupart des activités n'auront que quelques mots-clés, je n'ai donc pas vu le besoin d'une autre table pour cela. Cependant, je n'ai jamais vraiment pensé à faire ce que vous avez suggéré ... ce serait probablement un changement pour le mieux! Merci. – James

+0

J'ai une syntaxe stricte pour les mots-clés et c'est comme vous l'avez suggéré, c'est-à-dire mot-clé, mot-clé, mot-clé. Cependant, j'ai décidé d'aller avec l'autre table. Merci. – James

8

Votre problème est que LINQ-to-Entites doit traduire tout ce que vous lui donnez en SQL pour l'envoyer à la base de données.

Si c'est vraiment ce que vous devez faire, vous devrez forcer LINQ-to-Entities à retirer toutes les données et LINQ-to-Objects pour évaluer la condition.

Ex:

var aKeyword = "ACT"; 
var results = from a in db.Activities.ToList() 
       where a.Keywords.Split(',').Contains(aKeyword) == true 
       select a; 

Sachez cependant, que cela va retirer tous les objets de la table Activités. Une autre solution peut être de laisser le DB faire un peu d'un filtre initial, et filtrer sur le reste du chemin après:

var aKeyword = "ACT"; 
var results = (from a in db.Activities 
       where a.Keywords.Contains(aKeyword) 
       select a).ToList().Where(a => a.KeyWords.Split(',').Contains(aKeyword)); 

qui permettra LINQ à entités faire le filtre, il comprend (string.Contains devient une requête similaire) qui va filtrer certaines des données, puis appliquer le filtre réel que vous voulez via LINQ-to-Objects une fois que vous avez les objets de retour. L'appel ToList() force LINQ-to-Entities à exécuter la requête et à générer les objets, ce qui permet à LINQ-to-Objects d'être le moteur qui exécute la seconde partie de la requête.

+1

@James - oui, le premier vous coûtera pas mal de performance. Ce dernier est moins important (il nécessitera un scan table/index, mais ne retournera que les lignes candidates). Si c'est quelque chose que vous utiliserez beaucoup, je vous recommande de stocker les mots-clés dans la base de données prédéfinie (c'est-à-dire un tableau avec une ligne par mot-clé par activité - suggestion de Yannick). Ensuite, vous pouvez l'écrire comme une requête que la base de données peut gérer correctement. –

+0

Ouais je suis d'accord, j'ai décidé d'utiliser la suggestion de Yannick! – James

+0

Lors de l'utilisation de cette approche, je suggère de faire juste deux matchs, donc tout le traitement se passe toujours côté serveur: où a.Keywords.Contains ("," + un mot-clé) || a.Keywords.Contains (aKeyword + ",") –

0

Ma conjecture est la façon dont vous appelez Split. Cela devrait prendre un tableau. Peut-être il y a un autre Split en Linq il est de trouver et de vous donner une erreur inhabituelle:

Cela fonctionne pour LINQ to Objects:

var dataStore = new List<string> 
        { 
         "foo,bar,zoo", 
         "yelp,foo", 
         "fred", 
         "" 
        }; 
var results = from a in dataStore 
       where a.Split(new[] {','}).Contains("foo") 
       select a; 

foreach (var result in results) 
{ 
    Console.WriteLine("Match: {0}", result); 
} 

produit les éléments suivants:

Match: foo,bar,zoo 
Match: yelp,foo 

En fait, en pensant à ça, avez-vous besoin de la scission du tout? a.Contains("foo") peut être suffisant pour vous (sauf si vous ne voulez pas frapper foobar).

+0

Ouais defo besoin de la scission (comme vous l'avez mentionné) mots-clés peuvent faire partie d'autres mots-clés. Besoin d'une correspondance exacte – James

Questions connexes