Wir versuchen, den Entsprechungsoperator "LIKE" in Entity Framework für unsere Entitäten mit String-Feldern zu implementieren, es scheint jedoch nicht unterstützt zu werden. Hat jemand anderes versucht, so etwas zu tun?
Dieser Blogbeitrag fasst das Problem zusammen, das wir haben. Wir könnten enthalten verwenden, aber das trifft nur auf den trivialsten Fall für LIKE zu. Das Kombinieren von enthält, startswith, endswith und indexof bringt uns dorthin, erfordert jedoch eine Übersetzung zwischen Standard-Wildcards und Linq to Entities-Code.
Dies ist jetzt ein alter Beitrag, aber für jeden, der nach der Antwort sucht, sollte dieser Link helfen.
Kurze Version:
SqlFunctions.PatIndex - Methode - Gibt die Startposition des ersten Vorkommens eines Musters in einem angegebenen Ausdruck zurück oder Nullen, wenn das Muster nicht gefunden wird, für alle gültigen Text- und Zeichendatentypen
Namespace: System.Data.Objects.SqlClient Assembly: System.Data.Entity (in System.Data.Entity.dll)
Eine kleine Erklärung erscheint auch in diesem Forumsthread .
Ich kenne eigentlich nichts über EF, aber in LINQ to SQL formulieren Sie normalerweise eine LIKE-Klausel mit String.Contains:
where entity.Name.Contains("xyz")
wird übersetzt in
WHERE Name LIKE '%xyz%'
(Verwenden Sie StartsWith
und EndsWith
für anderes Verhalten.)
Ich bin nicht ganz sicher, ob das hilfreich ist, denn ich verstehe nicht, was Sie meinen, wenn Sie sagen, Sie versuchen, implementieren LIKE. Wenn ich mich völlig falsch verstanden habe, lass es mich wissen und ich lösche diese Antwort :)
Ich hatte das gleiche Problem.
Im Moment habe ich mich mit der clientseitigen Wildcard/Regex-Filterung basierend auf http://www.codeproject.com/Articles/11556/Converting-Wildcards-to-Regexes?msg=1423024#xx1423024xx - entschieden einfach und funktioniert wie erwartet.
Ich habe eine andere 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/de-de/library/system.data.objects.sqlclient.sqlfunctions.patindex.aspx
So was:
var q = EFContext.Products.Where(x => SqlFunctions.PatIndex("%CD%BLUE%", x.ProductName) > 0);
Hinweis: Diese Lösung ist nur für SQL-Server, da sie nicht standardmäßige PATINDEX-Funktionen verwendet.
Update: In EF 6.2 gibt es einen Like-Operator
Where(i => DbFunctions.Like(searchstring ,like expression)
In Entity Framework Core 2.0
wird der Operator LIKE
hinzugefügt:
var query = from e in _context.Employees
where EF.Functions.Like(e.Title, "%developer%")
select e;
Im Vergleich zu ... where e.Title.Contains("developer") ...
wird es wirklich in SQL
LIKE
übersetzt, anstatt in CHARINDEX
, die wir für Contains
-Methode sehen.
Sie wird in der Dokumentation als Teil von Entity SQL ausdrücklich erwähnt. Bekommen Sie eine Fehlermeldung?
// 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%'
wenn Sie MS SQL verwenden, habe ich 2 Erweiterungsmethoden geschrieben, um das% -Zeichen für die Platzhaltersuche zu unterstützen. (LinqKit ist erforderlich)
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;
}
}
verwendungszweck
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();
in ef6 und es sollte zu übersetzen
....
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 '
Für EfCore ist hier ein Beispiel zum Erstellen eines LIKE-Ausdrucks
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"));
Sie können einen Real wie in Link to Entities verwenden
Hinzufügen
<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>
zu Ihrem EDMX in diesem Tag:
edmx: Edmx/edmx: Laufzeit/edmx: ConceptualModels/Schema
Denken Sie auch an den Namespace im <schema namespace="" />
-Attribut
Fügen Sie dann eine Erweiterungsklasse im obigen Namespace hinzu:
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");
}
}
Diese Erweiterungsmethode wird jetzt der EDMX-Funktion zugeordnet.
Mehr Infos hier: http://jendaperl.blogspot.be/2011/02/like-in-linq-to-entities.html
re: "Wir möchten auf blah blah foo bar foo? bar? foo * bar? und anderen komplexen Mustern passen." Ich habe das eigentlich nicht probiert (nicht nötig) bis jetzt), aber haben Sie versucht, System.Text.RegularExpressions.RegEx zu verwenden?