38 votes

Utilisation de contains() dans LINQ to SQL

J'essaie d'implémenter une recherche par mot-clé très basique dans une application en utilisant linq-to-sql. Mes termes de recherche sont dans un tableau de chaînes de caractères, chaque élément du tableau étant un mot, et je voudrais trouver les lignes qui contiennent les termes de recherche. Je veux trouver les lignes qui contiennent les termes recherchés. Je me fiche qu'elles contiennent plus que les termes recherchés (ce sera très probablement le cas), mais tous les termes recherchés doivent être présents.

Idéalement, je voudrais quelque chose de similaire au snippet ci-dessous, mais je sais que cela ne fonctionnera pas. J'ai également consulté cette question ici mais l'auteur de cette question semble se contenter de faire les choses dans l'autre sens ( query.Contains(part.partName) ), ce qui ne fonctionne pas pour moi.

public IQueryable<Part> SearchForParts(string[] query)
{
    return from part in db.Parts
           where part.partName.Contains(query)
           select part;
}

Comment puis-je réécrire cette requête pour qu'elle fasse ce dont j'ai besoin ?

42voto

leppie Points 67289

Regarder les autres tentatives m'attriste :(

public IQueryable<Part> SearchForParts(string[] query)
{
  var q = db.Parts.AsQueryable(); 

  foreach (var qs in query)
  { 
    var likestr = string.Format("%{0}%", qs);
    q = q.Where(x => SqlMethods.Like(x.partName, likestr));
  }

  return q;
}

Hypothèses :

  • partName ressemble à : "ABC 123 XYZ"

  • La requête est { "ABC", "123", "XY" }

20voto

Trisped Points 2571

Une solution plus simple et plus correcte (que celle de Leppie) :

public IQueryable<Part> SearchForParts(string[] query)
{
    var q = db.Parts.AsQueryable(); 

    foreach (string qs in query)
    {
        q = q.Where(x => x.partName.Contains(qs));
    }

    return q;
}

Cela fonctionnera tant que partName est une chaîne (ou un équivalent SQL d'une chaîne).

La chose importante à noter est partName.Contains(qs) est différent de query.Contains(partName) .
Avec partName.Contains(qs) , partName est recherchée pour toute occurrence de qs . Le SQL résultant serait équivalent (où <qs> est la valeur de qs ) :

select * from Parts where partName like '%<qs>%';

A noter également StartsWith y EndsWith qui sont similaires à Contains mais cherchez la chaîne dans l'emplacement spécifique.

query.Contains(partName) est identique à un SQL in commande. Le SQL résultant serait équivalent à (où <query0> est la valeur de query[0] <query1> est la valeur de query[1] et <queryN> est la dernière valeur du tableau de requêtes) :

select * from Parts where partName in ( <query0>, <query1>, ..., <queryN> );

Mise à jour :
Il est également important de noter que la réponse de leppie n'échappe pas les caractères génériques avant de les ajouter à la chaîne de caractères. comme déclaration. Ce n'est pas un problème avec le Contains solution puisque Linq échappera la requête avant de l'envoyer. Une version échappée de la requête SqlMethods.Like La solution serait :

public IQueryable<Part> SearchForParts(string[] query)
{
    var q = db.Parts.AsQueryable(); 

    foreach (var qs in query)
    {
        string escaped_bs = qs.Replace("/", "//"),
            escaped_us = escaped_bs.Replace("_", "/_"),
            escaped_p = escaped_us.Replace("%", "/%"),
            escaped_br = escaped_p.Replace("[", "/["),
            likestr = string.Format("%{0}%", escaped_br);

        q = q.Where(x => SqlMethods.Like(x.partName, likestr, '/'));
    }

    return q;
}

Vous n'avez pas à vous soucier de ' puisque Linq s'en charge pour vous.

2voto

Rory Points 1786

Vous pouvez essayer :

public IQueryable<Part> SearchForParts(string[] query)
{
    return from part in db.Parts
           where query.All(term => part.partName.Contains(term))
           select part;
}

Cependant, je ne suis pas sûr que LINQ to SQL soit capable de le transformer en T-SQL. Une autre option serait :

public IQueryable<Part> SearchForParts(string[] query)
{
    var result = from part in db.Parts
                 select part;

    foreach(var term in query)
    {
        result = from part in result
                 where part.partName.Contains(term)
                 select part;
    }

    return result;
}

Ce n'est pas aussi joli, mais ça devrait marcher. Vous obtiendrez une requête avec beaucoup de AND dans la clause where.

1voto

Fadrian Sudaman Points 4659

Vous pouvez l'écrire comme ceci

var result = db.Parts.Where(p => query.All(q => p.partName.Contains(q)));

1voto

NinjaNye Points 3734

Utilisation de la NinjaNye.SearchExtension Le paquet nuget vous permet d'effectuer cette recherche en toute simplicité :

string[] terms = new[]{"search", "term", "collection"};
var result = db.Parts.Search(terms, p => p.PartName);

Vous pouvez également rechercher plusieurs propriétés de chaîne

var result = db.Parts.Search(terms, p => p.PartName, p.PartDescription);

Ou effectuer un RankedSearch qui renvoie IQueryable<IRanked<T>> qui comprend simplement une propriété qui indique combien de fois les termes de recherche sont apparus :

//Perform search and rank results by the most hits
var result = db.Parts.RankedSearch(terms, p => p.PartName, p.PartDescription)
                     .OrderByDescending(r = r.Hits);

Il existe un guide plus complet sur la page GitHub du projet : https://github.com/ninjanye/SearchExtensions

J'espère que cela aidera les futurs visiteurs

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