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
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);
}
Vous pouvez utiliser SqlMethods.Comme () .
Un exemple d'utilisation:
var results =
from u in users
where SqlMethods.Like(u.FirstName, "%John%")
select u;
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;
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;
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.
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.
Vous pouvez également utiliser "contient"
var myresult = db.MyItems.Where(x=>x.MyField.Contains(mysearchstring));
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.
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"));
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
Parlez-vous LINQ aux objets ou LINQ à SQL?
Pour LINQ aux objets, vous devrez recourir à expressions régulières me pense.