Recherche générique pour LINQ

Je voudrais savoir s'il est possible de faire une recherche générique en utilisant LINQ.

Je vois LINQ a Contains, StartsWith, EndsWith, etc.

Que faire si je veux quelque chose comme % Test si%ça marche%, Comment puis-je le faire?

Cordialement

32
demandé sur PlayKid 2009-06-24 23:18:27

12 réponses

J'utiliserais des Expressions régulières, car vous n'utiliseriez pas toujours Linq en SQL.

Comme cet exemple de Linq aux objets

List<string> list = new List<string>();
list.Add("This is a sentence.");
list.Add("This is another one.");
list.Add("C# is fun.");
list.Add("Linq is also fun.");

System.Text.RegularExpressions.Regex regEx = new System.Text.RegularExpressions.Regex("This");

var qry = list
    .Where<string>(item => regEx.IsMatch(item))
    .ToList<string>();

// Print results
foreach (var item in qry)
{
    Console.WriteLine(item);
}
32
répondu David Basarab 2009-06-24 19:27:14

Vous pouvez utiliser SqlMethods.Comme () .

Un exemple d'utilisation:

var results =
        from u in users
        where SqlMethods.Like(u.FirstName, "%John%")
        select u;
74
répondu Ryan Versaw 2009-06-24 19:22:56

Ajouter Le Système.Données.Linq.SqlClient à votre liste using ou imports puis essayez:

var results= from x in data
             where SqlMethods.Like(x.SearchField, “%something%like%this%”)
             select x;
14
répondu Joe Davis 2009-06-24 19:26:37

Pour Entity Framework Core 2.0 il est LIKE opérateur (annoncé en août 2017):

var query = from e in _context.Employees
                    where EF.Functions.Like(e.Title, "%developer%")
                    select e;
4
répondu Dmitry Pavlov 2017-10-10 13:22:36
var result = (from x in db.Members
              where x.IDNumber.Contains(idnumber)
              && x.InstitutionIdentifier == institution.Identifier
              select x).ToList();
return result;

Fonctionnera à la fois pour Linq to SQL et Linq en mémoire.

3
répondu Gerhard 2012-10-08 19:58:30

En regardant la question

Que faire si je veux quelque chose comme % Test si%ça marche%, Comment puis-je le faire?

Alors je m'attends à quelque chose de

LIKE '%Test if%it work%'

Ce qui Signifie que la chaîne doit contenir 'Tester si' et de 'fonctionner', , dans l'ordre.

Cela ne fonctionnera pas:

context.SomeTable.Where(s => s.Name.Contains("Test if%it work")).ToList();

Et si j'utilise:

context.SomeTable.Where(s => s.Name.Contains("Test if") && s.Name.Contains("it work")).ToList();

Ensuite, je vais trouver tous les enregistrements qui contiennent à la fois "Test if" et "it work", mais pas spécifiquement dans cet ordre .

Donc avec Contient ce n'est pas possible. Mais avec IndexOf {[24] } c'est.

IndexOf localisera la chaîne de recherche et retournera sa position dans la chaîne. Permet de trouver les mots dans le bon ordre.

-- Mise à jour --

Avec ma réponse originale, ce n'était pas mon but de fournir une solution générique, mais plutôt un exemple d'une autre approche qui n'est pas sql dependend. Il est donc correct que l'exemple original ne réponde qu'à la question littérale. Mais depuis la réponse peut être plus utile si elle est générique, j'ai écrit une extension IQuerable qui permet d'ajouter une instruction like À la requête aussi facile qu'une instruction where. L'extension fonctionne à la fois pour Linq comme Linq-Sql.

Cela trouvera tous les enregistrements avec "Test if" et "IT work", dans cet ordre.

context.SomeTable.Like("test if%it work", "Name").ToList();

listOfString.Like("test if%it work").ToList();

Extension, permet un nombre quelconque de caractères génériques:

/// <summary>
/// Allow to search the string with wildcards.
/// </summary>
/// <typeparam name="T">String or an object with a string member.</typeparam>
/// <param name="q">Original query</param>
/// <param name="searchstring">The searchstring</param>
/// <param name="memberName">The name of the field or null if not a field.</param>
/// <returns>Query filtered by 'LIKE'.</returns>
public static IQueryable<T> Like<T>(this IQueryable<T> q, string searchstring, string memberName = null)
{
    // %a%b%c% --> IndexOf(a) > -1 && IndexOf(b) > IndexOf(a) && IndexOf(c) > IndexOf(b)

    var eParam = Expression.Parameter(typeof(T), "e");

    MethodInfo methodInfo;

    // Linq (C#) is case sensitive, but sql isn't. Use StringComparison ignorecase for Linq.
    // Sql however doesn't know StringComparison, so try to determine the provider.
    var isLinq = (q.Provider.GetType().IsGenericType && q.Provider.GetType().GetGenericTypeDefinition() == typeof(EnumerableQuery<>));
    if (isLinq)
        methodInfo = typeof(string).GetMethod("IndexOf", new[] { typeof(string), typeof(StringComparison) });
    else
        methodInfo = typeof(string).GetMethod("IndexOf", new[] { typeof(string) });

    Expression expr;
    if (string.IsNullOrEmpty(memberName))
        expr = eParam;
    else
        expr = Expression.Property(eParam, memberName);

    // Split the searchstring by the wildcard symbol:
    var likeParts = searchstring.Split(new char[] { '%' }, StringSplitOptions.RemoveEmptyEntries);

    for (int i = 0; i < likeParts.Length; i++)
    {
        MethodCallExpression e;
        if (isLinq)
            e = Expression.Call(expr, methodInfo, new Expression[] { Expression.Constant(likeParts[i], typeof(string)), Expression.Constant(StringComparison.OrdinalIgnoreCase) });
        else
            e = Expression.Call(expr, methodInfo, Expression.Constant(likeParts[i], typeof(string)));

        if (i == 0)
        {
            // e.IndexOf("likePart") > -1
            q = q.Where(Expression.Lambda<Func<T, bool>>(Expression.GreaterThan(e, Expression.Constant(-1, typeof(int))), eParam));
        }
        else
        {
            // e.IndexOf("likePart_previous")
            MethodCallExpression ePrevious;
            if (isLinq)
                ePrevious = Expression.Call(expr, methodInfo, new Expression[] { Expression.Constant(likeParts[i - 1], typeof(string)), Expression.Constant(StringComparison.OrdinalIgnoreCase) });
            else
                ePrevious = Expression.Call(expr, methodInfo, Expression.Constant(likeParts[i - 1], typeof(string)));

            // e.IndexOf("likePart_previous") < e.IndexOf("likePart")
            q = q.Where(Expression.Lambda<Func<T, bool>>(Expression.LessThan(ePrevious, e), eParam));
        }
    }
    return q;
}

Comme il n'a pas besoin de SqlMethods, je suppose que vous pouvez l'utiliser pour n'importe quelle base de données, comme MySql ou Postgresql. Mais Je ne sais pas pour sûr. J'ai testé cela avec Sql Server en utilisant Entity Framework 6. L'instruction ci-dessus génère le code suivant dans Sql Server.

SELECT [Extent1].* FROM SomeTable AS [Extent1]
WHERE ((( CAST(CHARINDEX(N'test if', [Extent1].[Name]) AS int)) - 1) > -1)
AND ((( CAST(CHARINDEX(N'test if', [Extent1].[Name]) AS int)) - 1) < 
     (( CAST(CHARINDEX(N'it work', [Extent1].[Name]) AS int)) - 1))

A propos de la performance, il semble y avoir une discussion sur ce qui est "meilleur": LIKE ou CHARINDEX. Et d'après ce que J'ai lu, CHARINDEX semble être favori.

3
répondu Ruard van Elburg 2017-03-02 02:24:26
.Where( column LIKE "Pattern")
2
répondu Rony 2009-06-24 19:23:52

Vous pouvez également utiliser "contient"

var myresult = db.MyItems.Where(x=>x.MyField.Contains(mysearchstring));
2
répondu spadelives 2013-07-10 23:11:08

Je sais que c'est un vieux sujet, mais voici ma solution très simple:

string s=Regex.Escape("pattern - escaped for sanity").Replace("%", ".*").Replace("_", ".?");
user => Regex.IsMatch(user.FullName, s, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);

Dans ce code, j'utilise des caractères d'échappement communs pour le langage SQL. Si vous voulez utiliser say * et ?, la chaîne échappée contiendra \* et \? en conséquence, assurez-vous d'inclure le caractèreantislash dans les instructions .Replace(...). Bien sûr, si vous voulez donner à votre Utilisateur la possibilité de rexex search, n'échappez pas à la chaîne de motif.

Recherche Regex tutoriel pour d'autres options.

Je crois que normalement % trouvera , au moins un caractère, tandis que la RegEx .* trouvera zéro ou plus caractères. Donc, en réalité, le caractère générique % ressemble plus à .+ (gourmand) qu'à .* (paresseux).

J'espère que cela aide.

2
répondu nurchi 2014-01-09 01:29:46

Je ne sais pas si vous parlez LinqToSql ou simplement linq... mais vous pouvez des expressions régulières comme ceci:

.Where(dto => System.Text.RegularExpressions.Regex.IsMatch(dto.CustomerName, @"Ad"));
1
répondu bytebender 2009-06-24 19:26:16

Dans le code. Net, y compris LINQ aux objets, j'utilise l'implémentation de IsSqlLikeMatch fonction de thread en utilisant Regex pour créer une fonction similaire "like" de SQL..

Exemple d'utilisation

bool ret = message.IsSqlLikeMatch(pattern);

Plus de détails dans mon post les modèles "like" de SQL à comparer dans. Net

1
répondu Michael Freidgeim 2012-11-04 16:57:58

Parlez-vous LINQ aux objets ou LINQ à SQL?

Pour LINQ aux objets, vous devrez recourir à expressions régulières me pense.

0
répondu fretje 2009-06-24 19:24:25