LINQ'daki LIKE operatörü


90

C # LINQ ifadesindeki dizeleri SQL LIKEoperatörüne benzer şekilde karşılaştırmanın herhangi bir yolu var mı ?

Dize listem olduğunu varsayalım. Bu listede bir dizge aramak istiyorum. SQL'de şunu yazabilirim:

SELECT * FROM DischargePort WHERE PortName LIKE '%BALTIMORE%'

Yukarıdakinin yerine, sorgu bir linq sözdizimi ister.

using System.Text.RegularExpressions;
…

var regex = new Regex(sDischargePort, RegexOptions.IgnoreCase);
var sPortCode = Database.DischargePorts
                .Where(p => regex.IsMatch(p.PortName))
                .Single().PortCode;

Yukarıdaki LINQ sözdizimim çalışmıyor. Neyi yanlış anladım?


1
Bu sorgu, sizin koyduğunuz gibi, benim için esasen işe yaradı. Ancak, MongoDb Linq sürücüsünü kullanıyorum ve her Linq sağlayıcısında uygulama farklılıkları var ... neyse, Teşekkürler.
Mark Ewer

Bu, LINQ'da bulduğum en iyi çözüm. Teşekkürler. - @ Pranay-Rana
Abhishek Tomar

Ne istediğiniz ve neyin "işe yaramadığı" gerçekten net değil. LINQ-to-nesnelere eşdeğer mi Like, yoksa Likekullandığınız ORM'de çeviren bir işlev mi istiyorsunuz? İkincisi ise, hangi ORM? Ayrıca, size yardımcı olduysa, lütfen yanıtlardan birini kabul edin. İnsanlar, ne sorduğunuza dair belirsiz bir varsayımdan başka hiçbir şeye dayanmadan cevaplar biriktirmeye devam ediyor.
Gert Arnold

Yanıtlar:


144

Genellikle String.StartsWith/ EndsWith/ kullanırsınız Contains. Örneğin:

var portCode = Database.DischargePorts
                       .Where(p => p.PortName.Contains("BALTIMORE"))
                       .Single()
                       .PortCode;

Yine de LINQ to SQL aracılığıyla düzgün düzenli ifadeler yapmanın bir yolu olup olmadığını bilmiyorum. (Gerçekten hangi sağlayıcıyı kullandığınıza bağlı olduğuna dikkat edin - LINQ to Objects'te sorun olmaz; bu, sağlayıcının çağrıyı yerel sorgu biçimine, örneğin SQL'e dönüştürebilmesi meselesidir.)

DÜZENLEME: BitKFu'nun dediği gibi, tam olarak bir sonuç Singlebeklediğinizde kullanılmalıdır - bunun olmaması için bir hata olduğunda. Seçenekler , ya bağlı kullanılmalıdır tam olarak bekleneni.SingleOrDefaultFirstOrDefaultFirst


arkadaş ama bir sorun var, listemde "BALTIMORE" var ve verilen karşılaştırma parametrem "BALTIMORE [MD], US". Yukarıdaki sözdizimi seçilemiyor.
shamim

2
Aşağıdaki ifademe bir göz atın, Single () yönteminden gelebilir. FirstOrDefault () kullanmak daha iyidir
BitKFu

3
@shamim: Yani verileriniz aradığınız dizeyi içermiyor mu? Bunun SQL'de bile çalışmasını nasıl beklersiniz?
Jon Skeet

SQL'de sonuç kümesi elde edemezsiniz - C # 'da bir istisna alırsınız. Sonuç yok yerine biraz farklı olan. Bu yüzden FirstOrDefault'u kullanmanızı tavsiye ettim.
BitKFu

Bir başlangıç noktasından @BitKFu Single(), SingleOrDefault()biz ... tam bağlamda anlamak sürece, sonraki adım olacaktır
Marc Gravell

34

Regex? Hayır. Ancak bu sorgu için şunları kullanabilirsiniz:

 string filter = "BALTIMORE";
 (blah) .Where(row => row.PortName.Contains(filter)) (blah)

Eğer varsa , gerçekten SQL istiyorum LIKEkullanabileceğiniz System.Data.Linq.SqlClient.SqlMethods.Like(...)LINQ to SQL eşler, hangi LIKESQL Server.


@Maslow - uzmanlık alanım değil, korkarım - ama bunu tüm EF uygulamalarına eşlemenin güzel ve temiz bir yolu olduğuna inanmıyorum, bu yüzden ... hayır.
Marc Gravell

2
bu, SQL uygulamalarında işe yarayabilir, ancak standart bir nesne koleksiyonuyla çalışmaz
Chris McGrath 24'13

13

Şey ... bazen kullanmak rahatsız edici olabilir Contains, StartsWithveya EndsWithözellikle değer belirleme ifadesini ararken LIKEörneğin geçilen 'değer%' geliştiriciden StartsWithifadede fonksiyonun kullanılmasını gerektirir . Bu yüzden IQueryablenesneler için uzantı yazmaya karar verdim .

Kullanım

// 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

Kod

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

Birlikte çalışan bir sürümünüz var IEnumerablemı?
Nicke Manarin

9

Yerel LINQ'da, Contains/StartsWith/EndsWithveya RegExp kombinasyonunu kullanabilirsiniz .

LINQ2SQL'de kullanım yöntemi SqlMethods.Like()

    from i in db.myTable
    where SqlMethods.Like(i.field, "tra%ata")
    select i

Bu özelliği kullanmak için Assembly: System.Data.Linq (System.Data.Linq.dll dosyasında) ekleyin.


OP'nin aslında Linq2SQL demediğini anlıyorum, ancak ima edilmiş görünüyordu. Burada olmamın sebebi olduğunu StartsWith(), Contains()vb, do not Linq2SQL (ile çalışma en azından ben almak "... ... LINQ ifadesi çevrilemeyen" ve "müşteri değerlendirilmesi" için kullanılması ToList için bir talimat () -ki ben' zaten yapıyorum. Unutmayın, EF Core'daEF.Functions.Like()
Auspex

8

Jon Skeet ve Marc Gravell'in daha önce de bahsettiği gibi, basitçe bir içerme koşulu alabilirsiniz. Ancak benzer sorgunuz durumunda, Single () ifadesi almak çok tehlikelidir, çünkü bu yalnızca 1 sonuç bulduğunuz anlamına gelir. Daha fazla sonuç alırsanız, güzel bir istisna alırsınız :)

Bu yüzden Single () yerine FirstOrDefault () kullanmayı tercih ederim:

var first = Database.DischargePorts.FirstOrDefault(p => p.PortName.Contains("BALTIMORE"));
var portcode = first != null ? first.PortCode : string.Empty;

Eğer tam olarak bir eşleşme olduğu iddia edilen beklentimiz ise, Tek "tehlikeli" değildir - "doğru" dur. Her şey veriler hakkında iddia ettiğimiz şeye bağlı ... "herhangi bir sayı", "en az bir", "en fazla bir", "tam olarak bir", vb
Marc Gravell

3
bağlama bağlı olarak, olabilir ... tamamen sorgunun beklentisine bağlıdır
Marc Gravell

"Boş" veya "%" aramasına ne dersiniz? Bu "B", "BALT" ve "" ile başa çıkabilir mi (yani bana her şeyi getir)?
BlueChippy

3

Bu kadar basit

string[] users = new string[] {"Paul","Steve","Annick","Yannick"};    
var result = from u in users where u.Contains("nn") select u;

Sonuç -> Annick, Yannick


3

İdeal olarak StartWithveya kullanmalısınız EndWith.

İşte bir örnek:

DataContext  dc = new DCGeneral();
List<Person> lstPerson= dc.GetTable<Person>().StartWith(c=> c.strNombre).ToList();

return lstPerson;

2

Tek yöntemi bir yüklemle çağırabilirsiniz:

var portCode = Database.DischargePorts
                   .Single(p => p.PortName.Contains("BALTIMORE"))
                   .PortCode;

2
  .Where(e => e.Value.StartsWith("BALTIMORE"))

Bu, SQL'deki "LIKE" gibi çalışır ...


9
hayır .. hayır bu sadece LIKE 'terim%' gibi çalışmaz, bu da bir bütün olarak benzer operatör gibi çalışmaktan uzaktır ve joker karakterleri desteklemez
Chris McGrath

1
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();

bir göz alarak düşünün iyi asnwer yazmayı
Abdelillah AISSANI

0
   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;

0

Sadece dize nesnesi genişletme yöntemlerini ekleyin.

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

kullanım:

use namespase that contains this class;

var sPortCode = Database.DischargePorts
            .Where(p => p.PortName.Contains(new string [] {"BALTIMORE"},  StringComparison.CurrentCultureIgnoreCase) )
            .Single().PortCode;

0

@adobrzyc bu harika özel LIKEişleve sahipti - sadece IEnumerablesürümünü paylaşmak istedim .

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) });

    private static 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).Compile();
    }

    public static IEnumerable<TSource> Like<TSource, TMember>(this IEnumerable<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;
    }
}

0

Uzantı Linq / SQL gibi

LikeExtension Sınıfı

.NET 5'te test edilmiştir

 public static class LikeExtension {

    private static string ColumnDataBase<TEntity, TKey>(IModel model, Expression<Func<TEntity, TKey>> predicate) where TEntity : class {

        ITable table = model
            .GetRelationalModel()
            .Tables
            .First(f => f
                .EntityTypeMappings
                .First()
                .EntityType == model
                .FindEntityType(predicate
                    .Parameters
                    .First()
                .Type
            ));

        string column = (predicate.Body as MemberExpression).Member.Name;
        string columnDataBase = table.Columns.First(f => f.PropertyMappings.Count(f2 => f2.Property.Name == column) > 0).Name;

        return columnDataBase;

    }

    public static IQueryable<TEntity> Like<TEntity, TKey>(this DbContext context, Expression<Func<TEntity, TKey>> predicate, string text) where TEntity : class {

        string columnDataBase = ColumnDataBase(context.Model, predicate);
        return context.Set<TEntity>().FromSqlRaw(context.Set<TEntity>().ToQueryString() + " WHERE [" + columnDataBase + "] LIKE {0}", text);

    }

    public static async Task<IEnumerable<TEntity>> LikeAsync<TEntity, TKey>(this DbContext context, Expression<Func<TEntity, TKey>> predicate, string text, CancellationToken cancellationToken) where TEntity : class {

        string columnDataBase = ColumnDataBase(context.Model, predicate);
        return await context.Set<TEntity>().FromSqlRaw(context.Set<TEntity>().ToQueryString() + " WHERE [" + columnDataBase + "] LIKE {0}", text).ToListAsync(cancellationToken);

    }

    public static async Task<IEnumerable<TEntity>> LikeAsync<TEntity, TKey>(this IQueryable<TEntity> query, Expression<Func<TEntity, TKey>> predicate, string text, CancellationToken cancellationToken) where TEntity : class {

        DbSet<TEntity> entities = query as DbSet<TEntity>;
        string columnDataBase = ColumnDataBase(entities.EntityType.Model, predicate);
        return await entities.FromSqlRaw(query.ToQueryString() + " WHERE [" + columnDataBase + "] LIKE {0}", text).ToListAsync(cancellationToken);

    }

    public static IQueryable<TEntity> Like<TEntity, TKey>(this IQueryable<TEntity> query, Expression<Func<TEntity, TKey>> predicate, string text) where TEntity : class {

        DbSet<TEntity> entities = query as DbSet<TEntity>;
        string columnDataBase = ColumnDataBase(entities.EntityType.Model, predicate);
        return entities.FromSqlRaw(query.ToQueryString() + " WHERE [" + columnDataBase + "] LIKE {0}", text);

    }

}

Depo

    public async Task<IEnumerable<TEntity>> LikeAsync<TKey>(Expression<Func<TEntity, TKey>> predicate, string text, CancellationToken cancellationToken) {

        return await context.LikeAsync(predicate, text, cancellationToken);

    }

    public IQueryable<TEntity> Like<TKey>(Expression<Func<TEntity, TKey>> predicate, string text) {

        return context.Like(predicate, text);

    }

Kullanım

 IQueryable<CountryEntity> result = countryRepository
     .Like(k => k.Name, "%Bra[sz]il%") /*Use Sync*/
     .Where(w => w.DateRegister < DateTime.Now) /*Example*/
     .Take(10); /*Example*/

Veya

 IEnumerable<CountryEntity> result = await countryRepository
     .LikeAsync(k => k.Name, "%Bra[sz]il%", cancellationToken); /*Use Async*/

Veya

 IQueryable<CountryEntity> result = context.Countries
     .Like(k => k.Name, "%Bra[sz]il%")
     .Where(w => w.Name != null); /*Example*/

Veya

 List<CountryEntity> result2 = await context.Countries
     .Like(k => k.Name, "%Bra[sz]il%")
     .Where(w => w.Name != null) /*Example*/
     .ToListAsync(); /*Use Async*/

Veya

 IEnumerable<CountryEntity> result3 = await context.Countries
     .Where(w => w.Name != null)
     .LikeAsync(k => k.Name, "%Bra[sz]il%", cancellationToken); /*Use Async*/

0

.Net5'te test edilen EF işlevini de kullanabilirsiniz.

public async Task<IEnumerable<District>> SearchDistrict(string query, int stateId)
        {
            return await _dbContext
                .Districts
                .Include(s => s.State)
                .Where(s => s.StateId == stateId && EF.Functions.Like(s.Name, "$%{query}%"))
                .AsNoTracking()
                .ToListAsync();
        }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.