像实体框架中的运算符一样?


91

我们正在尝试在Entity Framework中为具有字符串字段的实体实现“ LIKE”运算符,但似乎不支持该运算符。有没有其他人试图做这样的事情?

这篇博客文章总结了我们遇到的问题。我们可以使用contains,但是只匹配LIKE最简单的情况。包含,startswith,endswith和indexof的组合可以使我们到达那里,但是需要在标准通配符和Linq to Entities代码之间进行转换。


1
如果您已经在使用EF 6.2.x,请转到此答案。为了这个答案,如果你正在使用EF核心2.X
CodeNotFound

Answers:


35

现在这是一篇旧文章,但是对于任何寻求答案的人,此链接都可以提供帮助。如果您已经在使用EF 6.2.x,请转到此答案。为了这个答案,如果你正在使用EF核心2.X

精简版:

SqlFunctions.PatIndex方法-在所有有效的文本和字符数据类型上,返回指定表达式中图案首次出现的起始位置,如果找不到该图案,则返回零

命名空间:System.Data.Objects.SqlClient程序集:System.Data.Entity(在System.Data.Entity.dll中)

论坛主题中也出现了一些解释。


59
链接到一个MSDN论坛的链接(该链接又链接到以下问题的答案)如何?
Eonasdan

答案是使用SqlFunctions.PatIndex方法。链接的论坛线程将提供更多“背景”信息。
扬·杜兰

下面的答案对于简单的模式是很好的,但是如果我想说“ WHERE Name LIKE'abc [0-9]%'”或其他更复杂的模式,仅使用Contains()并不能完全解决问题。
HotN

1
此问题的较旧答案的重复项。(不是其第一部分,而是其替代解决方案。)
Frédéric16年

154

我真的对EF一无所知,但是在LINQ to SQL中,您通常使用String来表达LIKE子句。

where entity.Name.Contains("xyz")

转换为

WHERE Name LIKE '%xyz%'

(用于StartsWithEndsWith其他行为。)

我不确定是否有帮助,因为当您说要实施 LIKE 时,我不明白您的意思。如果我完全误解了,请告诉我,我将删除此答案:)


4
请注意,“ WHERE Name LIKE'%xyz%'”将无法使用索引,因此,如果表很大,它的性能可能不会很好...
Mitch Wheat

1
好吧,我们希望能够匹配blah * blah foo bar foo?bar?foo bar?和其他复杂的模式。我们当前的方法类似于您提到的方法,我们可以使用contains,indexof,startswith,endswith等将这些查询转换为操作。我只是希望有一个更通用的解决方案。
brien

2
并不是我所知道的-我怀疑复杂的模式最终会更特定于db,并且很难以一般方式表示。
乔恩·斯基特

4
@Jon Skeet:就我所知,LIKE功能是ANSI标准的,在SQL Server,Oracle和DB2中几乎相同。
AK

2
使用这些运算符和MS SQL时,我看到的一件事是EF将它们添加为转义参数“ Name LIKE @ p__linq__1 ESCAPE N''〜''”,在我非常有限的用例中,其执行速度要慢得多,如果搜索字符串只是在查询中,例如“%xyz%”之类的名称。对于这种情况,我仍在使用StartsWith和Contains,但是我通过动态linq进行操作,因为这会将参数注入到SQL语句中,在我的情况下该语句会产生一个更高效的查询。不确定这是否是EF 4.0。也可以使用ObjectQueryParameters实现相同的功能……
Shane Neuville 2012年

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/zh-cn/library/system.data.objects.sqlclient.sqlfunctions.patindex.aspx

像这样:

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

注意:此解决方案仅适用于SQL Server,因为它使用了非标准的PATINDEX函数。


当PatIndex“起作用”时,它会再次咬住您,where子句中的PatIndex不使用您要过滤的列上的索引。
BlackICE 2014年

@BlackICE这是预期的。当您搜索内部文本(%CD%BLUE%)时,服务器将无法使用索引。只要有可能,从头开始搜索文本(CD%BLUE%)就会更有效率。
surfen 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 LIKE而不是CHARINDEX我们看到的Contains方法。


5

在文档中作为实体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()选项。
Stephen Newman

1
编译正常,但在运行时失败。
布赖恩2009年

我发布的代码在运行时失败?它来自Microsoft链接。
罗伯特·哈维

我使用指向描述我们遇到的相同问题的博客文章的链接来编辑问题。
布赖恩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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.