Reputation: 4440
We're trying to implement the "LIKE" operator in Entity Framework for our entities with string fields, but it doesn't appear to be supported. Has anyone else tried to do something like this?
This blog post summarizes the issue we're having. We could use contains, but that only matches the most trivial case for LIKE. Combining contains, startswith, endswith, and indexof gets us there, but requires a translation between standard wildcards and Linq to Entities code.
Upvotes: 119
Views: 178236
Reputation: 944
Update: In EF 6.2 there is a like operator
Where(obj => DbFunctions.Like(obj.Column , "%expression%"))
Upvotes: 55
Reputation: 119
For EfCore here is a sample to build LIKE expression
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"));
Upvotes: 6
Reputation: 3879
This is an old post now, but for anyone looking for the answer, this link should help. Go to this answer if you are already using EF 6.2.x. To this answer if you're using EF Core 2.x
Short version:
SqlFunctions.PatIndex method - returns the starting position of the first occurrence of a pattern in a specified expression, or zeros if the pattern is not found, on all valid text and character data types
Namespace: System.Data.Objects.SqlClient Assembly: System.Data.Entity (in System.Data.Entity.dll)
A bit of an explanation also appears in this forum thread.
Upvotes: 44
Reputation: 28290
There is LIKE
operator is added in Entity Framework Core 2.0
:
var query = from e in _context.Employees
where EF.Functions.Like(e.Title, "%developer%")
select e;
Comparing to ... where e.Title.Contains("developer") ...
it is really translated to SQL
LIKE
rather than CHARINDEX
we see for Contains
method.
Upvotes: 51
Reputation: 1048
You can use a real like in Link to Entities quite easily
Add
<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>
to your EDMX in this tag:
edmx:Edmx/edmx:Runtime/edmx:ConceptualModels/Schema
Also remember the namespace in the <schema namespace="" />
attribute
Then add an extension class in the above 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");
}
}
This extension method will now map to the EDMX function.
More info here: http://jendaperl.blogspot.be/2011/02/like-in-linq-to-entities.html
Upvotes: 0
Reputation: 1396
if you're using MS Sql, I have wrote 2 extension methods to support the % character for wildcard search. (LinqKit is required)
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;
}
}
usage
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 and it should translate to
....
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'
Upvotes: 2
Reputation: 4692
I had the same problem.
For now, I've settled with client-side Wildcard/Regex filtering based on http://www.codeproject.com/Articles/11556/Converting-Wildcards-to-Regexes?msg=1423024#xx1423024xx - it's simple and works as expected.
I've found another discussion on this topic: http://forums.asp.net/t/1654093.aspx/2/10
This post looks promising if you use Entity Framework >= 4.0:
Use SqlFunctions.PatIndex:
http://msdn.microsoft.com/en-us/library/system.data.objects.sqlclient.sqlfunctions.patindex.aspx
Like this:
var q = EFContext.Products.Where(x => SqlFunctions.PatIndex("%CD%BLUE%", x.ProductName) > 0);
Note: this solution is for SQL-Server only, because it uses non-standard PATINDEX function.
Upvotes: 38
Reputation: 180787
It is specifically mentioned in the documentation as part of Entity SQL. Are you getting an error message?
// 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
Upvotes: 5
Reputation: 1499770
I don't know anything about EF really, but in LINQ to SQL you usually express a LIKE clause using String.Contains:
where entity.Name.Contains("xyz")
translates to
WHERE Name LIKE '%xyz%'
(Use StartsWith
and EndsWith
for other behaviour.)
I'm not entirely sure whether that's helpful, because I don't understand what you mean when you say you're trying to implement LIKE. If I've misunderstood completely, let me know and I'll delete this answer :)
Upvotes: 180