Existe-t-il un moyen de comparer les chaînes dans une expression C # LINQ similaire à l'opérateur LIKE
de SQL?
Supposons que j'ai une liste de chaînes. Sur cette liste, je veux rechercher une chaîne. En SQL, je pourrais écrire:
SELECT * FROM DischargePort WHERE PortName LIKE '%BALTIMORE%'
Au lieu de ce qui précède, recherchez une syntaxe linq.
using System.Text.RegularExpressions;
…
var regex = new Regex(sDischargePort, RegexOptions.IgnoreCase);
var sPortCode = Database.DischargePorts
.Where(p => regex.IsMatch(p.PortName))
.Single().PortCode;
Ma syntaxe LINQ ci-dessus ne fonctionne pas. Qu'est-ce que je me suis trompé?
En général, vous utilisez String.StartsWith
/EndsWith
/Contains
. Par exemple:
var portCode = Database.DischargePorts
.Where(p => p.PortName.Contains("BALTIMORE"))
.Single()
.PortCode;
Je ne sais pas s'il existe un moyen de faire les expressions régulières appropriées via LINQ to SQL cependant. (Notez que cela dépend vraiment du fournisseur que vous utilisez. Cela conviendrait dans LINQ to Objects; le problème est de savoir si le fournisseur peut convertir l'appel en son format de requête natif, par exemple SQL.)
EDIT: Comme le dit BitKFu, Single
devrait être utilisé quand vous vous attendez à exactement un résultat - quand c'est une erreur de ne pas le faire. Les options de SingleOrDefault
, FirstOrDefault
ou First
doivent être utilisées selon exactement ce qui est attendu.
Regex? non. Mais pour cette requête , vous pouvez simplement utiliser:
string filter = "BALTIMORE";
(blah) .Where(row => row.PortName.Contains(filter)) (blah)
Si vous vraiment voulez SQL LIKE
, vous pouvez utiliser System.Data.Linq.SqlClient.SqlMethods.Like(...)
, que LINQ-to-SQL mappe à LIKE
dans SQL Server.
Eh bien ... parfois, il peut être inconfortable d’utiliser Contains
, StartsWith
ou EndsWith
surtout lors de la recherche de valeur, déterminer LIKE
déclaration, par exemple. passé 'valeur%' oblige le développeur à utiliser la fonction StartsWith
dans l'expression. J'ai donc décidé d'écrire l'extension pour les objets IQueryable
.
Usage
// numbers: 11-000-00, 00-111-00, 00-000-11
var data1 = parts.Like(p => p.Number, "%11%");
// result: 11-000-00, 00-111-00, 00-000-11
var data2 = parts.Like(p => p.Number, "11%");
// result: 11-000-00
var data3 = parts.Like(p => p.Number, "%11");
// result: 00-000-11
Code
public static class LinqEx
{
private static readonly MethodInfo ContainsMethod = typeof(string).GetMethod("Contains");
private static readonly MethodInfo StartsWithMethod = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
private static readonly MethodInfo EndsWithMethod = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
public static Expression<Func<TSource, bool>> LikeExpression<TSource, TMember>(Expression<Func<TSource, TMember>> property, string value)
{
var param = Expression.Parameter(typeof(TSource), "t");
var propertyInfo = GetPropertyInfo(property);
var member = Expression.Property(param, propertyInfo.Name);
var startWith = value.StartsWith("%");
var endsWith = value.EndsWith("%");
if (startWith)
value = value.Remove(0, 1);
if (endsWith)
value = value.Remove(value.Length - 1, 1);
var constant = Expression.Constant(value);
Expression exp;
if (endsWith && startWith)
{
exp = Expression.Call(member, ContainsMethod, constant);
}
else if (startWith)
{
exp = Expression.Call(member, EndsWithMethod, constant);
}
else if (endsWith)
{
exp = Expression.Call(member, StartsWithMethod, constant);
}
else
{
exp = Expression.Equal(member, constant);
}
return Expression.Lambda<Func<TSource, bool>>(exp, param);
}
public static IQueryable<TSource> Like<TSource, TMember>(this IQueryable<TSource> source, Expression<Func<TSource, TMember>> parameter, string value)
{
return source.Where(LikeExpression(parameter, value));
}
private static PropertyInfo GetPropertyInfo(Expression expression)
{
var lambda = expression as LambdaExpression;
if (lambda == null)
throw new ArgumentNullException("expression");
MemberExpression memberExpr = null;
switch (lambda.Body.NodeType)
{
case ExpressionType.Convert:
memberExpr = ((UnaryExpression)lambda.Body).Operand as MemberExpression;
break;
case ExpressionType.MemberAccess:
memberExpr = lambda.Body as MemberExpression;
break;
}
if (memberExpr == null)
throw new InvalidOperationException("Specified expression is invalid. Unable to determine property info from expression.");
var output = memberExpr.Member as PropertyInfo;
if (output == null)
throw new InvalidOperationException("Specified expression is invalid. Unable to determine property info from expression.");
return output;
}
}
Comme Jon Skeet et Marc Gravell ont déjà mentionné, vous pouvez simplement prendre une condition contient. Mais dans le cas d'une requête similaire, il est très dangereux de prendre une instruction Single (), car cela implique que vous ne trouviez qu'un résultat. Si vous obtenez plus de résultats, vous recevrez une exception Nice :)
Je préférerais donc utiliser FirstOrDefault () plutôt que Single ():
var first = Database.DischargePorts.FirstOrDefault(p => p.PortName.Contains("BALTIMORE"));
var portcode = first != null ? first.PortCode : string.Empty;
Dans LINQ natif, vous pouvez utiliser la combinaison de Contains/StartsWith/EndsWith
ou de RegExp.
Dans LINQ2SQL, utilisez la méthode SqlMethods.Like()
from i in db.myTable
where SqlMethods.Like(i.field, "tra%ata")
select i
ajoutez Assembly: System.Data.Linq (dans System.Data.Linq.dll) pour utiliser cette fonctionnalité.
Un simple comme ça
string[] users = new string[] {"Paul","Steve","Annick","Yannick"};
var result = from u in users where u.Contains("nn") select u;
Résultat -> Annick, Yannick
Vous pouvez appeler la méthode single avec un prédicat:
var portCode = Database.DischargePorts
.Single(p => p.PortName.Contains("BALTIMORE"))
.PortCode;
Idéalement, vous devriez utiliser StartWith
ou EndWith
.
Voici un exemple:
DataContext dc = new DCGeneral();
List<Person> lstPerson= dc.GetTable<Person>().StartWith(c=> c.strNombre).ToList();
return lstPerson;
.Where(e => e.Value.StartsWith("BALTIMORE"))
Cela fonctionne comme "J'aime" de SQL ...
List<Categories> categoriess;
private void Buscar()
{
try
{
categoriess = Contexto.Categories.ToList();
categoriess = categoriess.Where(n => n.CategoryID >= Convert.ToInt32(txtCatID.Text) && n.CategoryID <= Convert.ToInt32(txtCatID1.Text) && (n.CategoryName.Contains(txtCatName.Text)) ).ToList();
J'ai trouvé une solution pour imiter l'opérateur SQL LIKE. Reportez-vous à la réponse que j'ai écrite ici https://stackoverflow.com/a/46592475/118607
public static class StringEx
{
public static bool Contains(this String str, string[] Arr, StringComparison comp)
{
if (Arr != null)
{
foreach (string s in Arr)
{
if (str.IndexOf(s, comp)>=0)
{ return true; }
}
}
return false;
}
public static bool Contains(this String str,string[] Arr)
{
if (Arr != null)
{
foreach (string s in Arr)
{
if (str.Contains(s))
{ return true; }
}
}
return false;
}
}
var portCode = Database.DischargePorts
.Single(p => p.PortName.Contains( new string[] {"BALTIMORE"}, StringComparison.CurrentCultureIgnoreCase) ))
.PortCode;
Il suffit d’ajouter aux méthodes d’extension d’objet chaîne.
public static class StringEx
{
public static bool Contains(this String str, string[] Arr, StringComparison comp)
{
if (Arr != null)
{
foreach (string s in Arr)
{
if (str.IndexOf(s, comp)>=0)
{ return true; }
}
}
return false;
}
public static bool Contains(this String str,string[] Arr)
{
if (Arr != null)
{
foreach (string s in Arr)
{
if (str.Contains(s))
{ return true; }
}
}
return false;
}
}
usage:
use namespase that contains this class;
var sPortCode = Database.DischargePorts
.Where(p => p.PortName.Contains(new string [] {"BALTIMORE"}, StringComparison.CurrentCultureIgnoreCase) )
.Single().PortCode;