LINQのLIKE演算子


88

SQLのLIKE演算子に似たC#LINQ式の文字列を比較する方法はありますか?

文字列リストがあるとします。このリストで文字列を検索したいと思います。SQLでは、次のように書くことができます。

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

上記の代わりに、クエリはlinq構文を必要とします。

using System.Text.RegularExpressions;


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

上記のLINQ構文が機能しません。何がいけないのですか?


1
このクエリは、適切に配置することで基本的に機能しました。しかし、私はMongoDb Linqドライバーを使用しており、各Linqプロバイダーには実装の違いがあります...とにかく、ありがとうございます。
Mark Ewer 2013年

これは、LINQのように私が見つけた最良のソリューションです。ありがとう。-@ Pranay-Rana
Abhishek Tomar

回答:


140

一般的に使用しますString.StartsWith/ EndsWith/ Contains。例えば:

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

しかし、LINQ to SQLを介して適切な正規表現を行う方法があるかどうかはわかりません。(実際に使用しているプロバイダーに依存することに注意してください-LINQ to Objectsでは問題ありません。プロバイダーが呼び出しをネイティブクエリ形式(SQLなど)に変換できるかどうかが問題になります。)

編集:BitKFuが言うように、正確に1つの結果Singleを期待する場合に使用する必要があります-そうでない場合はエラーです。オプション、またはをに応じて使用する必要があります正確に予想されているもの。SingleOrDefaultFirstOrDefaultFirst


友だちですが、問題が1つあります。私のリストには「BALTIMORE」が含まれており、指定した比較パラメーターは「BALTIMORE [MD]、US」です。上記の構文は選択できません。
shamim 2011年

2
以下の私のステートメントを見てください、それはSingle()メソッドから来ているかもしれません。FirstOrDefault()を使用することをお
勧め

3
@shamim:では、データに探している文字列が含まれていないのですか?それがSQLでも機能することをどのように期待しますか?
Jon Skeet、2011年

SQLでは結果セットを取得できない場合があります-C#では例外を受け取ります。結果はなく、少し異なります。そのため、FirstOrDefaultを使用することをお勧めします。
BitKFu 2011年

開始時点から@BitKFu Single()SingleOrDefault()我々は完全なコンテキストを理解していない限り...、私の次のステップになります
マルクGravell

34

正規表現?番号。ただし、そのクエリでは次のように使用できます。

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

あなたがいる場合、実際に SQLをしたいLIKE、あなたが使用することができますSystem.Data.Linq.SqlClient.SqlMethods.Like(...)LINQ-に-SQLにマップされ、LIKESQL Serverで。


@Maslow-私の専門領域ではなく、私は恐れています-しかし、それをすべてのEF実装にマッピングするきれいな方法があるとは思わないので...いいえ。
Marc Gravell

2
これはSQL実装では機能しますが、標準のオブジェクトコレクションでは機能しません
Chris McGrath

13

ええと...時々それを使用するのContainsは不快かもしれません、StartsWithまたはEndsWith特に値を検索するとき、LIKE例えばStartsWith、式で関数を使用するために開発者から要求された 'value%'を渡してください。それで、IQueryableオブジェクトの拡張を書くことにしました。

使用法

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

コード

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

で動作するバージョンはありIEnumerableますか?
Nicke Manarin 2017

8

Jon SkeetとMarc Gravellがすでに述べたように、単純に包含条件をとることができます。ただし、likeクエリの場合、Single()ステートメントを使用することは非常に危険です。これは、結果が1つしかないことを意味するためです。さらに結果が出た場合、素晴らしい例外が表示されます:)

だから私はSingle()の代わりにFirstOrDefault()を使うことを好みます:

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

一致するものが1つだけであるという私たちの主張である場合、Singleは「危険」ではありません-「正しい」です。それはすべて、データについて私たちが主張していることに
帰着

3
コンテキストに応じて、それは...クエリの期待に完全に依存します
Marc Gravell

「空」または「%」検索はどうですか?これは「B」、「BALT」、「」(すべてを取得することを意味します)を処理できますか?
BlueChippy

8

ネイティブLINQでは、Contains/StartsWith/EndsWithまたはRegExpの組み合わせを使用できます。

LINQ2SQLでの使用方法 SqlMethods.Like()

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

この機能を使用するには、Assembly:System.Data.Linq(System.Data.Linq.dll内)を追加します。


OPが実際に Linq2SQLを言っていないことは理解していますが、それは暗黙のように思われました。私がここにいる理由は、それがあるStartsWith()Contains()などは、ないない Linq2SQL(と仕事少なくとも私が得る「... ... LINQ式を翻訳することができませんでした」と「クライアント評価」の利用ToListメソッドへの指示()-which I」 mはすでに実行していますEF.Functions.Like()
。EFCore

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

これはSQLの「LIKE」のように機能します...


8
いいえ、ありません。LIKE 'term%'のように機能するだけではなく、like演算子のように機能することはなく、ワイルドカードをサポートしていません
Chris McGrath

3

このようにシンプル

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

結果->アニック、ヤニック


2

述語で単一のメソッドを呼び出すことができます:

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

2

理想的には、StartWithまたはを使用する必要がありますEndWith

次に例を示します。

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

文字列オブジェクトの拡張メソッドに追加するだけです。

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

使用法:

use namespase that contains this class;

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.