web-dev-qa-db-ja.com

LINQのワイルドカード検索

LINQを使用してワイルドカード検索を実行できるかどうかを知りたいです。

LINQにはContains、StartsWith、EndsWithなどがあります。

%Test if%it work%のようなものが必要な場合、どうすればよいですか?

よろしく

35
PlayKid

いつもLinq to SQLを使用しているとは限らないので、正規表現を使用します。

このLinq to Objectsの例のように

List<string> list = new List<string>();
list.Add("This is a sentence.");
list.Add("This is another one.");
list.Add("C# is fun.");
list.Add("Linq is also fun.");

System.Text.RegularExpressions.Regex regEx = new System.Text.RegularExpressions.Regex("This");

var qry = list
    .Where<string>(item => regEx.IsMatch(item))
    .ToList<string>();

// Print results
foreach (var item in qry)
{
    Console.WriteLine(item);
}
34
David Basarab

SqlMethods.Like() を使用できます。

使用例:

var results =
        from u in users
        where SqlMethods.Like(u.FirstName, "%John%")
        select u;
77
Ryan Versaw

system.Data.Linq.SqlClientを使用リストまたはインポートリストに追加してから試してください:

var results= from x in data
             where SqlMethods.Like(x.SearchField, “%something%like%this%”)
             select x;
14
Joe Davis

にとって Entity Framework Core 2.0LIKE演算子があります( 2017年8月に発表 ):

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

質問を見て

%Test if%it work%のようなものが必要な場合、どうすればよいですか?

それから私は何かを期待しています

LIKE '%Test if%it work%'

つまり、文字列には「Test if」と「it work」、その順序でが含まれている必要があります。

これは機能しません:

context.SomeTable.Where(s => s.Name.Contains("Test if%it work")).ToList();

そして、私が使用する場合:

context.SomeTable.Where(s => s.Name.Contains("Test if") && s.Name.Contains("it work")).ToList();

次に、「テストif」と「それが機能する」の両方を含むすべてのレコードを検索しますが、、ただしその順序ではありません

Containsでは、これは不可能です。しかし、IndexOfではそうです。

IndexOfは、検索文字列を検索し、文字列内の検索文字列の位置を返します。正しい順序で単語を検索できるようにします。

-更新-

私の元の答えでは、一般的なソリューションを提供することは私の目標ではなく、SQL依存ではない別のアプローチの例です。したがって、元の例は文字通りの質問にのみ答えることは正しいです。しかし、答えが一般的な場合は答えがより便利になる可能性があるため、クエリにlikeステートメントをwhereステートメントと同じくらい簡単に追加できるIQuerable拡張機能を作成しました。この拡張機能は、LinqとLinq-Sqlの両方で機能します。

これにより、「Test if」と「it work」の両方を持つすべてのレコードがこの順序で検索されます。

context.SomeTable.Like("test if%it work", "Name").ToList();

listOfString.Like("test if%it work").ToList();

拡張、任意の数のワイルドカードを許可:

/// <summary>
/// Allow to search the string with wildcards.
/// </summary>
/// <typeparam name="T">String or an object with a string member.</typeparam>
/// <param name="q">Original query</param>
/// <param name="searchstring">The searchstring</param>
/// <param name="memberName">The name of the field or null if not a field.</param>
/// <returns>Query filtered by 'LIKE'.</returns>
public static IQueryable<T> Like<T>(this IQueryable<T> q, string searchstring, string memberName = null)
{
    // %a%b%c% --> IndexOf(a) > -1 && IndexOf(b) > IndexOf(a) && IndexOf(c) > IndexOf(b)

    var eParam = Expression.Parameter(typeof(T), "e");

    MethodInfo methodInfo;

    // Linq (C#) is case sensitive, but sql isn't. Use StringComparison ignorecase for Linq.
    // Sql however doesn't know StringComparison, so try to determine the provider.
    var isLinq = (q.Provider.GetType().IsGenericType && q.Provider.GetType().GetGenericTypeDefinition() == typeof(EnumerableQuery<>));
    if (isLinq)
        methodInfo = typeof(string).GetMethod("IndexOf", new[] { typeof(string), typeof(StringComparison) });
    else
        methodInfo = typeof(string).GetMethod("IndexOf", new[] { typeof(string) });

    Expression expr;
    if (string.IsNullOrEmpty(memberName))
        expr = eParam;
    else
        expr = Expression.Property(eParam, memberName);

    // Split the searchstring by the wildcard symbol:
    var likeParts = searchstring.Split(new char[] { '%' }, StringSplitOptions.RemoveEmptyEntries);

    for (int i = 0; i < likeParts.Length; i++)
    {
        MethodCallExpression e;
        if (isLinq)
            e = Expression.Call(expr, methodInfo, new Expression[] { Expression.Constant(likeParts[i], typeof(string)), Expression.Constant(StringComparison.OrdinalIgnoreCase) });
        else
            e = Expression.Call(expr, methodInfo, Expression.Constant(likeParts[i], typeof(string)));

        if (i == 0)
        {
            // e.IndexOf("likePart") > -1
            q = q.Where(Expression.Lambda<Func<T, bool>>(Expression.GreaterThan(e, Expression.Constant(-1, typeof(int))), eParam));
        }
        else
        {
            // e.IndexOf("likePart_previous")
            MethodCallExpression ePrevious;
            if (isLinq)
                ePrevious = Expression.Call(expr, methodInfo, new Expression[] { Expression.Constant(likeParts[i - 1], typeof(string)), Expression.Constant(StringComparison.OrdinalIgnoreCase) });
            else
                ePrevious = Expression.Call(expr, methodInfo, Expression.Constant(likeParts[i - 1], typeof(string)));

            // e.IndexOf("likePart_previous") < e.IndexOf("likePart")
            q = q.Where(Expression.Lambda<Func<T, bool>>(Expression.LessThan(ePrevious, e), eParam));
        }
    }
    return q;
}

SqlMethodsを必要としないため、MySqlやPostgresqlなどの任意のデータベースに使用できると思います。しかし、私は確かに知りません。 Entity Framework 6を​​使用してSql Serverでこれをテストしました。上記のステートメントは、Sql Serverで次のコードを生成します。

SELECT [Extent1].* FROM SomeTable AS [Extent1]
WHERE ((( CAST(CHARINDEX(N'test if', [Extent1].[Name]) AS int)) - 1) > -1)
AND ((( CAST(CHARINDEX(N'test if', [Extent1].[Name]) AS int)) - 1) < 
     (( CAST(CHARINDEX(N'it work', [Extent1].[Name]) AS int)) - 1))

パフォーマンスについては、「より良い」ものについての議論がいくつかあるようです:LIKEまたはCHARINDEX。そして、私が読んだことから、CHARINDEXはお気に入りのようです。

4
.Where( column LIKE "Pattern")
2
Rony
var result = (from x in db.Members
              where x.IDNumber.Contains(idnumber)
              && x.InstitutionIdentifier == institution.Identifier
              select x).ToList();
return result;

Linq to SQLとメモリ内のLinqの両方で動作します。

2
Gerhard

私はこれが古いトピックであることを知っていますが、ここに私の非常に簡単な解決策があります:

_string s=Regex.Escape("pattern - escaped for sanity").Replace("%", ".*").Replace("_", ".?");
user => Regex.IsMatch(user.FullName, s, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
_

このコードでは、SQL言語に共通のエスケープ文字を使用しています。 say _*_と_?_を使用する場合、エスケープされた文字列には_\*_と_\?_が含まれ、それに対応するバックスラッシュ文字.Replace(...)ステートメント。もちろん、ユーザーにRexEx検索機能を提供する場合は、パターン文字列をエスケープしないでください。

他のオプションについては、正規表現チュートリアルを検索してください。

通常、_%_は少なくとも1文字と一致しますが、正規表現_.*_はゼロ以上文字と一致します。したがって、実際には、_%_ワイルドカードは、_.+_(怠laz)ではなく、_.*_(貪欲)に似ています。

お役に立てれば。

2
nurchi

linqToSqlを話すのか、単にlinqを話すのかはわかりませんが、次のような正規表現が可能です。

.Where(dto => System.Text.RegularExpressions.Regex.IsMatch(dto.CustomerName, @"Ad"));
1
bytebender

LINQ to Objectsを含む.Netコードでは、スレッドのIsSqlLikeMatch関数の実装を使用しています Regexを使用してSQLの「ライク」を作成関数。

使用例

bool ret = message.IsSqlLikeMatch(pattern);

私の投稿の詳細 。Netで比較するSQLの「類似」パターン

1

「含む」を使用することもできます

var myresult = db.MyItems.Where(x=>x.MyField.Contains(mysearchstring));
1
spadelives

LINQ to objectsまたはLINQ to SQLを使用していますか?

LINQ toオブジェクトの場合、 正規表現 に頼らなければなりません。

0
fretje

これは、ユーザーの検索で「*」のワイルドカードフィルターをサポートするために使用します。 (順序は関係ありません):

 if (!string.IsNullOrEmpty(SearchString))
    {
     List<String> containValues = new List<String>();
     if (SearchString.Contains("*"))
        {

        String[] pieces = SearchString.Split("*");

        foreach (String piece in pieces)
                {
                if (piece != "")
                   {
                   containValues.Add(piece);
                   }
                 }
           }

       if (containValues.Count > 0)
          {
          foreach(String thisValue in containValues)
             {
             Items = Items.Where(s => s.Description.Contains(thisValue));
             }
           }
           else
           {
           Items = Items.Where(s => s.Description.Contains(SearchString));
           }
       }
0
pcalkins