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?

shamim
sumber
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.

Jon Skeet
sumber
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.

Marc Gravell
sumber
@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;
    }
}
adobrzyc.dll
sumber
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;
BitKFu
sumber
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.

Marat Batalandabad
sumber
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 ...

pengguna1930698
sumber
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

Yannick Turbang
sumber
2

Anda dapat memanggil metode tunggal dengan predikat:

var portCode = Database.DischargePorts
                   .Single(p => p.PortName.Contains("BALTIMORE"))
                   .PortCode;
Zebi
sumber
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;
Eduardo Romero Marin
sumber
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;
NoBrend s
sumber
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;
NoBrend s
sumber
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();
Eber Camacho
sumber
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;
    }
}
Steve
sumber