Entity Framework'deki Operator gibi mi?


93

Dize alanlarına sahip varlıklarımız için Entity Framework "LIKE" operatörünü uygulamaya çalışıyoruz, ancak desteklenmiyor gibi görünüyor. Başkası böyle bir şey yapmaya çalıştı mı?

Bu blog yazısı , yaşadığımız sorunu özetlemektedir. İçerir kullanabilirdik, ancak bu yalnızca LIKE için en önemsiz durumla eşleşiyor. Contains, startswith, endswith ve indexof öğelerinin birleştirilmesi bizi oraya götürür, ancak standart joker karakterler ile Linq to Entities kodu arasında bir çeviri gerektirir.


1
Zaten EF 6.2.x kullanıyorsanız bu yanıta gidin. To bu cevap size EF Çekirdek 2.x kullanıyorsanız
CodeNotFound

Yanıtlar:


36

Bu artık eski bir gönderi, ancak cevabı arayanlar için bu bağlantı yardımcı olmalı. Zaten EF 6.2.x kullanıyorsanız bu yanıta gidin. To bu cevap size EF Çekirdek 2.x kullanıyorsanız

Kısa versiyon:

SqlFunctions.PatIndex yöntemi - tüm geçerli metin ve karakter veri türlerinde, belirli bir ifadede bir modelin ilk oluşumunun başlangıç ​​konumunu veya model bulunamazsa sıfırları döndürür

Ad alanı: System.Data.Objects.SqlClient Assembly: System.Data.Entity (System.Data.Entity.dll içinde)

Bu forum başlığında bir miktar açıklama da yer alıyor .


59
bu soruya aşağıdaki yanıta geri bağlanan bir MSDN forumuna bağlanan cevap nasıl kabul edilir ?
Eonasdan

Yanıt, SqlFunctions.PatIndex yöntemini kullanmaktı. Bağlantılı forum başlığı biraz daha "arka plan" bilgisi sağlamaktı.
Yann Duran

Aşağıdaki cevap basit kalıplar için iyidir, ancak "WHERE Name GİBİ 'abc [0-9]%" veya başka bir karmaşık kalıp demek istersem, sadece Contains () kullanmak pek de yeterli olmaz.
2014

1
Bu soruya verilen bu eski yanıtın kopyası . (İlk bölümünden değil, alternatif çözümünden.)
Frédéric

154

EF hakkında gerçekten hiçbir şey bilmiyorum, ancak LINQ to SQL'de genellikle String kullanarak bir LIKE yan tümcesi ifade edersiniz.

where entity.Name.Contains("xyz")

Çevirir

WHERE Name LIKE '%xyz%'

( Diğer davranışlar için StartsWithve kullanın EndsWith.)

Bunun yararlı olup olmadığından tam olarak emin değilim, çünkü LIKE'ı uygulamaya çalıştığını söylerken ne demek istediğini anlamıyorum . Tamamen yanlış anladıysam bana bildirin ve bu yanıtı sileceğim :)


4
lütfen "WHERE Name GİBİ '% xyz%" bir indeksi kullanamayacağını unutmayın, bu nedenle tablo çok büyükse o kadar iyi performans göstermeyebilir ...
Mitch Wheat

1
Pekala, blah * blah foo bar foo? Bar? Foo bar? ve diğer karmaşık desenler. Şu anki yaklaşımımız bahsettiğinize benzer, bu sorguları içerir, indexof, startswith, endswith vb. Kullanarak işlemlere dönüştürürdük. Sadece daha genel amaçlı bir çözüm olmasını umuyordum.
brien

2
Farkında olduğumdan değil - Karmaşık kalıpların daha fazla db'ye özgü olduğunu ve genel bir şekilde ifade etmenin zor olduğunu düşünüyorum.
Jon Skeet

4
@Jon Skeet: Bildiğim kadarıyla LIKE işlevi ANSI standardındadır ve SQL Server, Oracle ve DB2'de hemen hemen aynıdır.
AK

2
Bu işleçleri ve MS SQL'i kullanırken gördüğüm bir şey, EF'nin bunları kaçan parametreler olarak eklemesidir. sadece "% xyz% 'gibi ad" sorgusunda. Sahip olduğum senaryolar için hala StartsWith ve Contains kullanıyorum ancak bunu dinamik linq aracılığıyla yapıyorum çünkü bu benim senaryomda bir daha verimli sorgu. Bunun EF 4.0 olup olmadığından emin değilim. Aynı şeyi elde etmek için ObjectQueryParameters'ı da kullanabilirsiniz ...
Shane Neuville

35

Ben de aynı sorunu yaşadım.

Şimdilik, http://www.codeproject.com/Articles/11556/Converting-Wildcards-to-Regexes?msg=1423024#xx1423024xx'e dayalı istemci tarafı Wildcard / Regex filtrelemesine karar verdim - basit ve şu şekilde çalışıyor: beklenen.

Bu konuyla ilgili başka bir tartışma buldum: http://forums.asp.net/t/1654093.aspx/2/10
Entity Framework> = 4.0 kullanıyorsanız bu gönderi umut verici görünüyor:

SqlFunctions.PatIndex kullanın:

http://msdn.microsoft.com/en-us/library/system.data.objects.sqlclient.sqlfunctions.patindex.aspx

Bunun gibi:

var q = EFContext.Products.Where(x =>
SqlFunctions.PatIndex("%CD%BLUE%", x.ProductName) > 0);

Not: Bu çözüm, standart olmayan PATINDEX işlevini kullandığından yalnızca SQL-Server içindir.


PatIndex "çalışırken" sizi ısırmak için geri dönecektir, where cümlesindeki PatIndex filtrelemek istediğiniz sütundaki indeksleri kullanmaz.
BlackICE

@BlackICE bu bekleniyor. İç metinde (% CD% BLUE%) arama yaptığınızda, sunucu dizinleri kullanamayacaktır. Mümkün olduğunda, baştan itibaren metin aramak (CD% BLUE%) daha etkilidir.
surfen

@surfen patindex bundan daha kötüdür, önünde% olmasa bile dizini kullanmaz, patindex ile (BLUE CD%) araması sütun dizinini kullanmaz.
BlackICE

23

Güncelleme: EF 6.2'de benzer bir operatör var

Where(obj => DbFunctions.Like(obj.Column , "%expression%")

Bu daha net bir örnek olmaz: Where(obj => DbFunctions.Like(obj.Column , "%expression%")?
DCD

Kesinlikle öyle. Değiştirildi
Lode Vlaeminck

20

Orada LIKEoperatör eklenir Entity Framework Core 2.0:

var query = from e in _context.Employees
                    where EF.Functions.Like(e.Title, "%developer%")
                    select e;

Onunla karşılaştırmak, yöntem olarak görmekten çok ... where e.Title.Contains("developer") ..., gerçekten çevrilmiştir .SQL LIKECHARINDEXContains


5

Belgelerde Varlık SQL'in bir parçası olarak özellikle belirtilmiştir. Bir hata mesajı mı alıyorsunuz?

// LIKE and ESCAPE
// If an AdventureWorksEntities.Product contained a Name 
// with the value 'Down_Tube', the following query would find that 
// value.
Select value P.Name FROM AdventureWorksEntities.Product 
    as P where P.Name LIKE 'DownA_%' ESCAPE 'A'

// LIKE
Select value P.Name FROM AdventureWorksEntities.Product 
    as P where P.Name like 'BB%'

http://msdn.microsoft.com/en-us/library/bb399359.aspx


1
Gelecekte EF'den uzaklaşmak istemeniz durumunda Entity SQL'den uzak durmak isterim. Güvenli bir şekilde oynayın ve orijinal yanıtta Contains (), StartsWith () ve EndsWith () seçeneklerine bağlı kalın.
Stephen Newman

1
Bu iyi derler, ancak çalışma zamanında başarısız olur.
brien

Gönderdiğim kod çalışma zamanında başarısız mı? Microsoft bağlantısından gelir.
Robert Harvey

Soruyu, yaşadığımız sorunu açıklayan bir blog gönderisinin bağlantısıyla düzenledim.
brien

İçerir () sizin biletiniz gibi görünüyor. Ancak Jon Skeet'in belirttiği gibi, İçerir ihtiyaçlarınızı karşılamıyorsa, veritabanını doğrudan işleyen bazı gerçek SQL'e geçmeniz gerekebilir.
Robert Harvey

2

MS Sql kullanıyorsanız, joker arama için% karakterini desteklemek için 2 uzantı yöntemi yazdım. (LinqKit gereklidir)

public static class ExpressionExtension
{
    public static Expression<Func<T, bool>> Like<T>(Expression<Func<T, string>> expr, string likeValue)
    {
        var paramExpr = expr.Parameters.First();
        var memExpr = expr.Body;

        if (likeValue == null || likeValue.Contains('%') != true)
        {
            Expression<Func<string>> valExpr = () => likeValue;
            var eqExpr = Expression.Equal(memExpr, valExpr.Body);
            return Expression.Lambda<Func<T, bool>>(eqExpr, paramExpr);
        }

        if (likeValue.Replace("%", string.Empty).Length == 0)
        {
            return PredicateBuilder.True<T>();
        }

        likeValue = Regex.Replace(likeValue, "%+", "%");

        if (likeValue.Length > 2 && likeValue.Substring(1, likeValue.Length - 2).Contains('%'))
        {
            likeValue = likeValue.Replace("[", "[[]").Replace("_", "[_]");
            Expression<Func<string>> valExpr = () => likeValue;
            var patExpr = Expression.Call(typeof(SqlFunctions).GetMethod("PatIndex",
                new[] { typeof(string), typeof(string) }), valExpr.Body, memExpr);
            var neExpr = Expression.NotEqual(patExpr, Expression.Convert(Expression.Constant(0), typeof(int?)));
            return Expression.Lambda<Func<T, bool>>(neExpr, paramExpr);
        }

        if (likeValue.StartsWith("%"))
        {
            if (likeValue.EndsWith("%") == true)
            {
                likeValue = likeValue.Substring(1, likeValue.Length - 2);
                Expression<Func<string>> valExpr = () => likeValue;
                var containsExpr = Expression.Call(memExpr, typeof(String).GetMethod("Contains",
                    new[] { typeof(string) }), valExpr.Body);
                return Expression.Lambda<Func<T, bool>>(containsExpr, paramExpr);
            }
            else
            {
                likeValue = likeValue.Substring(1);
                Expression<Func<string>> valExpr = () => likeValue;
                var endsExpr = Expression.Call(memExpr, typeof(String).GetMethod("EndsWith",
                    new[] { typeof(string) }), valExpr.Body);
                return Expression.Lambda<Func<T, bool>>(endsExpr, paramExpr);
            }
        }
        else
        {
            likeValue = likeValue.Remove(likeValue.Length - 1);
            Expression<Func<string>> valExpr = () => likeValue;
            var startsExpr = Expression.Call(memExpr, typeof(String).GetMethod("StartsWith",
                new[] { typeof(string) }), valExpr.Body);
            return Expression.Lambda<Func<T, bool>>(startsExpr, paramExpr);
        }
    }

    public static Expression<Func<T, bool>> AndLike<T>(this Expression<Func<T, bool>> predicate, Expression<Func<T, string>> expr, string likeValue)
    {
        var andPredicate = Like(expr, likeValue);
        if (andPredicate != null)
        {
            predicate = predicate.And(andPredicate.Expand());
        }
        return predicate;
    }

    public static Expression<Func<T, bool>> OrLike<T>(this Expression<Func<T, bool>> predicate, Expression<Func<T, string>> expr, string likeValue)
    {
        var orPredicate = Like(expr, likeValue);
        if (orPredicate != null)
        {
            predicate = predicate.Or(orPredicate.Expand());
        }
        return predicate;
    }
}

kullanım

var orPredicate = PredicateBuilder.False<People>();
orPredicate = orPredicate.OrLike(per => per.Name, "He%llo%");
orPredicate = orPredicate.OrLike(per => per.Name, "%Hi%");

var predicate = PredicateBuilder.True<People>();
predicate = predicate.And(orPredicate.Expand());
predicate = predicate.AndLike(per => per.Status, "%Active");

var list = dbContext.Set<People>().Where(predicate.Expand()).ToList();    

ef6'da ve şu şekilde çevrilmelidir

....
from People per
where (
    patindex(@p__linq__0, per.Name) <> 0
    or per.Name like @p__linq__1 escape '~'
) and per.Status like @p__linq__2 escape '~'

', @ p__linq__0 ='% He% llo% ', @ p__linq__1 ='% Hi% ', @ p__linq_2 ='% Active '


Yorumunuz için teşekkürler Ronel, yardımcı olabileceğim bir şey var mı? hata mesajı nedir?
Steven Chong

2

EfCore için burada LIKE ifadesi oluşturmak için bir örnek var

protected override Expression<Func<YourEntiry, bool>> BuildLikeExpression(string searchText)
    {
        var likeSearch = $"%{searchText}%";

        return t => EF.Functions.Like(t.Code, likeSearch)
                    || EF.Functions.Like(t.FirstName, likeSearch)
                    || EF.Functions.Like(t.LastName, likeSearch);
    }

//Calling method

var query = dbContext.Set<YourEntity>().Where(BuildLikeExpression("Text"));

0

Varlıklara Bağlan'da gerçek bir beğeniyi oldukça kolay bir şekilde kullanabilirsiniz

Ekle

    <Function Name="String_Like" ReturnType="Edm.Boolean">
      <Parameter Name="searchingIn" Type="Edm.String" />
      <Parameter Name="lookingFor" Type="Edm.String" />
      <DefiningExpression>
        searchingIn LIKE lookingFor
      </DefiningExpression>
    </Function>

Bu etiketteki EDMX'inize:

edmx: Edmx / edmx: Çalışma Zamanı / edmx: ConceptualModels / Schema

Ayrıca <schema namespace="" />öznitelikteki ad alanını da unutmayın

Ardından yukarıdaki ad alanına bir uzantı sınıfı ekleyin:

public static class Extensions
{
    [EdmFunction("DocTrails3.Net.Database.Models", "String_Like")]
    public static Boolean Like(this String searchingIn, String lookingFor)
    {
        throw new Exception("Not implemented");
    }
}

Bu uzantı yöntemi şimdi EDMX işleviyle eşleşecek.

Daha fazla bilgi burada: http://jendaperl.blogspot.be/2011/02/like-in-linq-to-entities.html

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.