Frage an entity-framework, .net, sql-server, linq-to-entities – Gefällt dir Operator in Entity Framework?

79

Wir versuchen, den Operator "LIKE" in Entity Framework für unsere Entitäten mit Zeichenfolgenfeldern zu implementieren, aber dies scheint nicht unterstützt zu werden. Hat jemand anderes versucht, so etwas zu tun?

DieseBlogeintrag fasst das Problem, das wir haben, zusammen. Wir könnten enthält verwenden, aber das entspricht nur dem trivialsten Fall für LIKE. Das Kombinieren von enthält, startet mit, endet mit und indexof bringt uns dorthin, erfordert jedoch eine Übersetzung zwischen Standard-Platzhaltern und Linq to Entities-Code.

Gehe zudiese Antwort  Wenn Sie EF 6.2.x bereits verwenden. Zudiese Antwort wenn Sie EF Core 2.x verwenden CodeNotFound

Deine Antwort

9   die antwort
32

Im Moment habe ich mich mit der clientseitigen Wildcard / Regex-Filterung auf der Basis von zufrieden gegebenhttp://www.codeproject.com/Articles/11556/Converting-Wildcards-to-Regexes?msg=1423024#xx1423024xx - Es ist einfach und funktioniert wie erwartet.

Ich habe eine weitere Diskussion zu diesem Thema gefunden:http://forums.asp.net/t/1654093.aspx/2/10
Dieser Beitrag sieht vielversprechend aus, wenn Sie Entity Framework> = 4.0 verwenden:

Verwenden Sie SqlFunctions.PatIndex:

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

So was:

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

Hinweis: Diese Lösung ist nur für SQL-Server vorgesehen, da nicht standardmäßige PATINDEX-Funktionen verwendet werden.

Ich habe eine Belohnung bekommen, danke, dass du das gefunden hast! Tom Carver
@surfen patindex ist schlimmer als das, aber es wird den Index auch ohne% im Vordergrund nicht verwenden. Wenn Sie mit patindex nach (BLAUE CD%) suchen, wird der Spaltenindex nicht verwendet. BlackICE
@BlackICE das wird erwartet. Wenn Sie nach innerem Text suchen (% CD% BLUE%), kann der Server keine Indizes verwenden. Die Suche nach Text von Anfang an (CD% BLUE%) ist nach Möglichkeit effizienter. surfen
Während PatIndex "funktioniert", wird es Sie beißen, PatIndex in der where-Klausel verwendet nicht die Indizes für die Spalte, nach der Sie filtern möchten. BlackICE
2

habe ich 2 Erweiterungsmethoden geschrieben, um das% -Zeichen für die Platzhaltersuche zu unterstützen. (LinqKit wird benötigt)

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

Verwendungszweck

<code>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();    
</code>

in ef6 und es sollte zu übersetzen

<code>....
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 '~'
</code>

', @ p__linq__0 ='% He% llo% ', @ p__linq__1 ='% Hi% ', @ p__linq_2 ='% Active '

Ich habe Fehler in PredicateBuilder Ronel Gonzales
danke für deinen Kommentar Ronel, kann ich irgendetwas helfen? Was ist die Fehlermeldung? Steven Chong
144

aber in LINQ to SQL drücken Sie normalerweise eine LIKE-Klausel mit String.Contains aus:

<code>where entity.Name.Contains("xyz")
</code>

wird übersetzt in

<code>WHERE Name LIKE '%xyz%'
</code>

(BenutzenStartsWith undEndsWith für anderes Verhalten.)

Ich bin mir nicht ganz sicher, ob das hilfreich ist, weil ich nicht verstehe, was du meinst, wenn du sagst, dass du es versuchstimplementieren MÖGEN. Wenn ich völlig falsch verstanden habe, lass es mich wissen und ich werde diese Antwort löschen :)

@landsteven: Wann würdest du damit rechnenStartsWith oderEndsWith zurückgebentrue, aberContains falsch zurückgeben? Jon Skeet
@AlexKuznetsov: Ich erinnere mich an einige Probleme mit der Kompatibilität zwischen SQL Server und Oracle in der Vergangenheit, insbesondere wenn es um die Flucht geht. Ich musste einen Hibernate-Operator schreiben, um zu verstehen, dass% in der Abfragezeichenfolge enthalten ist, und die Oracle- und SQL Server-Implementierungen waren unterschiedlich. Es könnte sein, dass ich einen Trick verpasst habe oder dass sich die Welt seitdem verbessert hat. Jon Skeet
@ John Skeet: Wenn mein Benutzer "ABC Dealer" als Zeichenfolge eingibt, um meine Datenbank abzufragen und Ergebnisse für eine AutoCompleteBox.ItemsSource zurückzugeben. Die Ergebnisse müssen eine Liste aller relativen Datensätze enthalten, die in der Quelltabelle gefunden wurden, auch wenn der gesuchte tatsächliche Datensatz möglicherweise mit der angegebenen Eingabe beginnt ODER enthält ODER endet. landsteven
+1 Ich habe dies gerade mit EF 3.5 auf SQL Server ausprobiert, und es funktioniert zumindest in dieser Kombination. Mark Seemann
28

aber für alle, die nach der Antwort suchen,dieser Link sollte helfen. Gehe zudiese Antwort Wenn Sie EF 6.2.x bereits verwenden. Zudiese Antwort wenn Sie EF Core 2.x verwenden

Kurzfassung:

SqlFunctions.PatIndex method - Gibt die Startposition des ersten Auftretens eines Musters in einem angegebenen Ausdruck oder Nullen, wenn das Muster nicht gefunden wird, für alle gültigen Text- und Zeichendatentypen zurück

Namespace: System.Data.Objects.SqlClient Assembly: System.Data.Entity (in System.Data.Entity.dll)

Darin erscheint auch eine kleine ErklärungForum-Thread.

Die Antwort unten ist gut für einfache Muster, aber wenn ich "WHERE Name LIKE 'abc [0-9]%'" oder ein anderes komplexeres Muster sagen möchte, kann ich es nicht ganz ausschneiden, wenn ich einfach Contains () benutze. HotN
Wie ist die akzeptierte Antwort diejenige, die auf ein MSDN-Forum verlinkt, das auf diese Frage zurück auf das Forum verweist?Antwort unten? Eonasdan
Dup von diesem ÄlterenAntworten zu dieser Frage. (Nicht von seinem ersten Teil, sondern von seiner alternativen Lösung.) Frédéric
Die Antwort war die Verwendung der SqlFunctions.PatIndex-Methode. Der verlinkte Forenthread sollte ein bisschen mehr "Hintergrund" -Info liefern. Yann Duran
14

Update: In EF 6.2 gibt es einen ähnlichen Operator

<code>Where(i => DbFunctions.Like(searchstring ,like expression)
</code>
5

hnt. Erhalten Sie eine Fehlermeldung?

<code>// 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%'
</code>

http://msdn.microsoft.com/en-us/library/bb399359.aspx

Ein weiterer relevanter StackOverflow-Beitrag:stackoverflow.com/questions/835790/how-to-do-sql-like-in-linq Robert Harvey
Ich wäre versucht, mich von Entity SQL fernzuhalten, falls Sie sich in Zukunft von EF entfernen möchten. Gehen Sie auf Nummer sicher und bleiben Sie stattdessen bei den Optionen Contains (), StartsWith () und EndsWith () in der ursprünglichen Antwort. Stephen Newman
Das kompiliert gut, schlägt aber zur Laufzeit fehl. brien
Fehlermeldung?.. Robert Harvey
9

Es gibtLIKE Operator wird hinzugefügtEntity Framework Core 2.0:

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

Verglichen mit... where e.Title.Contains("developer") ... es ist wirklich übersetztSQL LIKE eher, alsCHARINDEX wir sehen fürContains Methode.

0

bla foobar foo? bar? foo * bar? und andere komplexe Muster. "Ich habe das noch nicht ausprobiert (ich habe es noch nicht gebraucht), aber haben Sie versucht, System.Text.RegularExpressions.RegEx zu verwenden?

Ein regulärer Ausdruck würde auf dem Webserver ausgeführt, nicht auf der Datenbank. Grundsätzlich müsste die gesamte Tabelle von SQL Server zurückgegeben und dann gefiltert werden. Das würde in einigen kleinen Fällen funktionieren, aber wir müssen große (ish) Datenmengen durchsuchen. Guter Vorschlag, das war auch mein erster Gedanke. brien
0

Hinzufügen

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

zu Ihrem EDMX in diesem Tag:

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

Denken Sie auch an den Namespace im<schema namespace="" /> Attribut

Fügen Sie dann eine Erweiterungsklasse im obigen Namespace hinzu:

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

Diese Erweiterungsmethode wird nun der EDMX-Funktion zugeordnet.

Mehr Infos hier:http://jendaperl.blogspot.be/2011/02/like-in-linq-to-entities.html

Verwandte Fragen