Entity Frameworkのオペレーターのようですか?


91

Entity Frameworkで文字列フィールドを持つエンティティの「LIKE」演算子を実装しようとしていますが、サポートされていないようです。他の誰かがこのようなことをしようとしましたか?

このブログ投稿は私たちが抱えている問題を要約しています。containsを使用することもできますが、これはLIKEの最も些細なケースにのみ一致します。contains、startswith、endswith、indexofを組み合わせるとアクセスできますが、標準のワイルドカードとLinq to Entitiesコード間の変換が必要です。


1
すでにEF 6.2.xを使用している場合は、この回答に進んでください。この答えあなたはEFコア2.xのを使用している場合
CodeNotFound

回答:


35

これは今は古い投稿ですが、答えを探している人は、このリンクが役立つはずです。EF 6.2.xをすでに使用している場合は、この回答に進んでください。この答えあなたはEFコア2.xのを使用している場合

短縮版:

SqlFunctions.PatIndexメソッド-すべての有効なテキストおよび文字データ型で、指定された式でパターンが最初に出現する開始位置、またはパターンが見つからない場合はゼロを返します

名前空間:System.Data.Objects.SqlClientアセンブリ:System.Data.Entity(System.Data.Entity.dll内)

このフォーラムのスレッドにも少し説明があります


59
受け入れられた回答は、この質問に以下回答にリンクしているMSDNフォーラムにリンクしている回答とはどのようなものですか?
Eonasdan 2012年

答えはSqlFunctions.PatIndexメソッドを使用することでした。リンクされたフォーラムスレッドは、もう少し「背景」情報を提供することでした。
Yann Duran 2014年

以下の答えは単純なパターンに適していますが、「WHERE Name LIKE 'abc [0-9]%'」またはその他のより複雑なパターンを言いたい場合は、単にContains()を使用しても完全にカットされません。
HotN 2014年

1
この質問に対するこの古い回答の重複。(その最初の部分ではなく、その代替ソリューションです。)
フレデリック

154

EFについては本当に何も知りませんが、LINQ to SQLでは、通常、String.Containsを使用してLIKE句を表現します。

where entity.Name.Contains("xyz")

に翻訳する

WHERE Name LIKE '%xyz%'

(使用StartsWithおよびEndsWithその他の動作。)

LIKE を実装しようとしていると言ったときの意味がわからないので、それが役立つかどうかは完全にはわかりません。完全に誤解している場合はお知らせください。この回答は削除します:)


4
「WHERE Name LIKE '%xyz%'」はインデックスを使用できないため、テーブルが巨大な場合、パフォーマンスが低下する可能性があることに注意してください...
Mitch Wheat

1
まあ、何とか*何とか foo bar foo?bar?foo bar?その他の複雑なパターン。私たちの現在のアプローチはあなたが述べたものに似ています、私たちはそれらのクエリをcontains、indexof、startswith、endswithなどを使用して操作に変換します。
brien、2009年

2
私が気づいているわけではありません-複雑なパターンは最終的にはdb固有のものになり、一般的な方法で表現するのが難しいと思います。
Jon Skeet、

4
@ジョン・スキート:私の知る限り、LIKE機能はANSI標準であり、SQL Server、Oracle、DB2でもほぼ同じです。
AK

2
これらの演算子とMS SQLの使用で見た1つのことは、EFがエスケープされたパラメーター "Name LIKE @ p__linq__1 ESCAPE N ''〜 ''"としてそれらを追加することです。 「%xyz%のような名前」というクエリに含まれています。シナリオでは、まだStartsWithとContainsを使用していますが、私のシナリオで生成するSQLステートメントにパラメーターを挿入するため、動的linqを介して実行します。より効率的なクエリです。これがEF 4.0かどうかがわかりません。ObjectQueryParametersを使用して同じことを実行することもできます...
Shane Neuville

34

私も同じ問題を抱えていました。

とりあえず、http://www.codeproject.com/Articles/11556/Converting-Wildcards-to-Regexes? msg = 1423024#xx1423024xxに基づくクライアント側のワイルドカード/正規表現フィルタリングで解決しました -シンプルで、次のように動作します期待された。

このトピックに関する別のディスカッションを見つけました:http : //forums.asp.net/t/1654093.aspx/2/10
この投稿は、Entity Framework> = 4.0を使用している場合に有望です。

SqlFunctions.PatIndexを使用します。

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

このような:

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

注:このソリューションは非標準のPATINDEX関数を使用するため、SQL-Server専用です。


PatIndexは「機能」しますが、where句のPatIndexはフィルタリングする列のインデックスを使用しません。
BlackICE 2014年

@BlackICEはこれが予想されています。内部テキスト(%CD%BLUE%)で検索すると、サーバーはインデックスを使用できなくなります。可能な限り、最初からテキストを検索する(CD%BLUE%)のがより効率的です。
2014年

@surfen patindexはそれよりも悪いですが、前に%がなくてもインデックスを使用しません。patindexで(BLUE CD%)を検索すると、列インデックスは使用されません。
BlackICE 2014年

22

更新:EF 6.2には、like演算子があります

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

これはより明確な例ではないでしょうWhere(obj => DbFunctions.Like(obj.Column , "%expression%")か?
DCD

確かです。変更
Lode Vlaeminck

19

あるLIKEオペレータがに追加されますEntity Framework Core 2.0

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

それを比較する... where e.Title.Contains("developer") ...ことはSQL LIKECHARINDEX私たちがContainsメソッドを見るのではなく、実際に翻訳されます。


5

これは、Entity SQLの一部としてドキュメントに具体的に記載されています。エラーメッセージが表示されますか?

// 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
将来的にEFから離れたい場合に備えて、Entity SQLから離れたいと思います。安全にプレイし、代わりに元の応答のContains()、StartsWith()、およびEndsWith()オプションを使用してください。
スティーブンニューマン

1
これは正常にコンパイルされますが、実行時に失敗します。
brien、2009年

投稿したコードは実行時に失敗しますか?それはマイクロソフトのリンクから来ています。
ロバートハーベイ、

私が抱えている同じ問題を説明するブログ投稿へのリンクを含む質問を編集しました。
brien、2009年

Contains()がチケットのようです。しかし、Jon Skeetが指摘したように、Containsがニーズを満たさない場合、データベースを直接操作する実際のSQLにドロップダウンする必要があるかもしれません。
ロバートハーベイ、

2

MS SQLを使用している場合、ワイルドカード検索で%文字をサポートする2つの拡張メソッドを作成しました。(LinqKitが必要です)

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

使用法

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では、次のように変換されます

....
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 '


コメントRonelに感謝します。何かお手伝いできることはありますか?エラーメッセージは何ですか?
Steven Chong

2

EfCoreの場合、ここにLIKE式を作成するサンプルがあります

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

エンティティへのリンクのように、本物のようなものを非常に簡単に使用できます

追加

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

このタグであなたのEDMXに:

edmx:Edmx / edmx:Runtime / edmx:ConceptualModels / Schema

<schema namespace="" />属性の名前空間も覚えておいてください

次に、上記の名前空間に拡張クラスを追加します。

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

この拡張メソッドは、EDMX関数にマップされます。

詳細はこちら:http : //jendaperl.blogspot.be/2011/02/like-in-linq-to-entities.html

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