SUKA operator di LINQ


89

Apakah ada cara untuk membandingkan string dalam ekspresi C # LINQ yang mirip dengan LIKEoperator SQL ?

Misalkan saya memiliki daftar string. Di daftar ini saya ingin mencari string. Di SQL, saya bisa menulis:

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

Alih-alih di atas, kueri menginginkan sintaks linq.

using System.Text.RegularExpressions;
…

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

Sintaks LINQ saya di atas tidak berfungsi. Apa yang salah?


1
Kueri ini pada dasarnya berfungsi untuk saya saat Anda memasukkannya. Tapi, saya menggunakan driver MongoDb Linq dan ada perbedaan implementasi di setiap penyedia Linq ... bagaimanapun, Terima kasih.
Mark Ewer

Ini adalah solusi terbaik yang saya temukan seperti di LINQ. Terima kasih. - @ Pranay-Rana
Abhishek Tomar

Jawaban:


143

Biasanya Anda menggunakan String.StartsWith/ EndsWith/ Contains. Sebagai contoh:

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

Saya tidak tahu apakah ada cara untuk melakukan ekspresi reguler yang tepat melalui LINQ ke SQL. (Perhatikan bahwa ini benar-benar bergantung pada penyedia mana yang Anda gunakan - akan baik-baik saja di LINQ ke Objek; ini masalah apakah penyedia dapat mengonversi panggilan ke dalam format kueri aslinya, misalnya SQL.)

EDIT: Seperti yang dikatakan BitKFu, Singleharus digunakan saat Anda mengharapkan tepat satu hasil - ketika itu adalah kesalahan untuk itu tidak terjadi. Pilihan dari SingleOrDefault, FirstOrDefaultatau Firstharus digunakan tergantung pada persis apa yang diharapkan.


teman tapi, ada satu masalah, daftar saya berisi "BALTIMORE", dan parameter perbandingan yang saya berikan adalah "BALTIMORE [MD], US". Sintaks di atas gagal untuk memilih.
shamim

2
lihat pernyataan saya di bawah ini, itu mungkin berasal dari metode Single (). Lebih baik menggunakan FirstOrDefault ()
BitKFu

3
@shamim: Jadi, data Anda tidak berisi string yang Anda cari? Bagaimana Anda mengharapkan itu untuk bekerja bahkan di SQL?
Jon Skeet

Dalam SQL Anda mungkin tidak mendapatkan hasil yang ditetapkan - di C # Anda akan menerima pengecualian. Yang sedikit berbeda, bukannya tidak ada hasil. Itulah mengapa saya merekomendasikan untuk menggunakan FirstOrDefault.
BitKFu

@BitKFu dari titik awal Single(), SingleOrDefault()akan menjadi langkah saya berikutnya, kecuali kita memahami konteks lengkapnya ...
Marc Gravell

34

Regex? tidak. Tetapi untuk kueri itu Anda bisa menggunakan:

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

Jika Anda benar - benar menginginkan SQL LIKE, Anda dapat menggunakan System.Data.Linq.SqlClient.SqlMethods.Like(...), yang dipetakan LINQ-ke-SQL LIKEdi SQL Server.


@Maslow - bukan bidang keahlian saya, saya khawatir - tetapi saya tidak percaya ada cara yang bagus untuk memetakannya ke semua penerapan EF, jadi ... tidak.
Marc Gravell

2
ini dapat bekerja pada implementasi SQL tetapi tidak bekerja dengan kumpulan objek standar
Chris McGrath

13

Yah ... kadang-kadang mungkin tidak nyaman untuk digunakan Contains, StartsWithatau EndsWithterutama ketika mencari nilai menentukan LIKEpernyataan misalnya lulus 'nilai%' mengharuskan dari pengembang untuk menggunakan StartsWithfungsi dalam ekspresi. Jadi saya memutuskan untuk menulis ekstensi untuk IQueryableobjek.

Pemakaian

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

Kode

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

Apakah Anda memiliki versi yang dapat digunakan IEnumerable?
Nicke Manarin

8

Seperti yang telah disebutkan Jon Skeet dan Marc Gravell, Anda dapat dengan mudah mengambil kondisi cadangan. Tetapi dalam kasus kueri like Anda, sangat berbahaya untuk mengambil pernyataan Single (), karena itu menyiratkan bahwa Anda hanya menemukan 1 hasil. Jika lebih banyak hasil, Anda akan menerima pengecualian yang bagus :)

Jadi saya lebih suka menggunakan FirstOrDefault () daripada Single ():

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

jika ekspektasi kami adalah bahwa hanya ada satu pertandingan, Single tidak "berbahaya" - itu "benar". Semuanya bermuara pada apa yang kami klaim tentang data ... "angka apa pun", "setidaknya satu", "paling banyak satu", "tepat satu", dll
Marc Gravell

3
tergantung pada konteks, itu bisa ... tergantung sepenuhnya pada ekspektasi kueri
Marc Gravell

Bagaimana dengan pencarian "kosong" atau "%"? Bisakah ini menangani "B", "BALT" dan "" (artinya memberi saya segalanya)?
BlueChippy

8

Dalam LINQ asli, Anda dapat menggunakan kombinasi Contains/StartsWith/EndsWithatau RegExp.

Dalam metode penggunaan LINQ2SQL SqlMethods.Like()

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

tambahkan Assembly: System.Data.Linq (di System.Data.Linq.dll) untuk menggunakan fitur ini.


Saya mengerti bahwa OP sebenarnya tidak mengatakan Linq2SQL, tetapi tampaknya tersirat. Alasan saya di sini adalah karena StartsWith(),, Contains()dll, tidak berfungsi dengan Linq2SQL (setidaknya saya mendapatkan "Ekspresi LINQ… tidak dapat diterjemahkan…" dan instruksi untuk menggunakan ToList () untuk "evaluasi klien" —yang saya ' Saya sudah melakukan. Catatan, di EF Core, dipindahkan keEF.Functions.Like()
Auspex

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

Ini berfungsi seperti "LIKE" dari SQL ...


8
tidak .. tidak itu tidak hanya bekerja seperti LIKE 'term%' yang jauh dari bekerja seperti operator like secara keseluruhan dan tidak mendukung wildcard
Chris McGrath

3

Sesederhana ini

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

Hasil -> Annick, Yannick


2

Anda dapat memanggil metode tunggal dengan predikat:

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

2

Idealnya Anda harus menggunakan StartWithatau EndWith.

Berikut ini contohnya:

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

return lstPerson;

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

Cukup tambahkan ke metode ekstensi objek string.

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

pemakaian:

use namespase that contains this class;

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

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

pertimbangkan untuk melihat bagaimana menulis
jawaban yang

0

@adobrzyc memiliki LIKEfungsi khusus yang hebat ini - Saya hanya ingin membagikan IEnumerableversinya.

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;
    }
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.