web-dev-qa-db-fra.com

Utilisation de contains () dans LINQ to SQL

J'essaie d'implémenter une recherche de mots clés très basique dans une application utilisant linq-to-sql. Mes termes de recherche sont dans un tableau de chaînes, chaque élément du tableau étant un mot, et je voudrais trouver les lignes qui contiennent les termes de recherche. Cela ne me dérange pas s'ils contiennent plus que les termes de recherche (très probablement, ils le seront), mais tous les termes de recherche doivent être présents.

Idéalement, j'aimerais quelque chose de similaire à l'extrait ci-dessous, mais je sais que cela ne fonctionnera pas. Aussi, j'ai regardé cette question ici , mais l'auteur de cette question semble content 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?

38
a_m0d

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"}

42
leppie

Une solution plus simple et plus correcte (puis 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érente de query.Contains(partName) .
Avec partName.Contains(qs), partName est recherché 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 et EndsWith qui sont similaires à Contains mais regardez pour la chaîne à l'emplacement spécifique.

query.Contains(partName) est identique à une commande SQL in . 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 dans le tableau de requête) :

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 aux caractères génériques avant de les ajouter à l'instruction comme . Ce n'est pas un problème avec la solution Contains puisque Linq échappera à la requête avant de l'envoyer. Une version échappée de la solution SqlMethods.Like 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 en préoccuper car Linq vous échappera.

20
Trisped

Tu pourrais 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 sais pas si LINQ to SQL pourra 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 ANDs dans la clause where.

2
Rory

Vous pouvez l'écrire comme ceci

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

L'utilisation du package nuget NinjaNye.SearchExtension vous permet d'effectuer facilement cette recherche:

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 effectuez un RankedSearch qui renvoie IQueryable<IRanked<T>> qui inclut simplement une propriété qui montre 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 des projets: https://github.com/ninjanye/SearchExtensions

J'espère que cela aidera les futurs visiteurs

1
NinjaNye

Je pense que cela est quelque peu simple et fonctionne pour moi:

string[] product = products.Split(','); 
using (var context = new ProjectTrackerEntities()) 
{ var result = from part in context.DBAudits where product.Contains(part.TableName) select part; }
0
murali