与实体框架中的运营商一样?

问题描述 投票:79回答:9

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

这个blog post总结了我们遇到的问题。我们可以使用contains,但这只匹配LIKE的最简单的情况。组合contains,startswith,endswith和indexof将我们带到那里,但需要在标准通配符和Linq to Entities代码之间进行转换。

.net sql-server entity-framework linq-to-entities
9个回答
29
投票

这是一个旧帖子,但对于任何寻找答案的人来说,this link应该有所帮助。如果您已经在使用EF 6.2.x,请访问this answer。如果您正在使用EF Core 2.x,请访问this answer

精简版:

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

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

在这个forum thread中也会出现一些解释。


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:运行/ EDMX:ConceptualModels /架构

还要记住<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


146
投票

我真的对EF一无所知,但在LINQ to SQL中,你通常使用String.Contains表达一个LIKE子句:

where entity.Name.Contains("xyz")

翻译成

WHERE Name LIKE '%xyz%'

(使用StartsWithEndsWith进行其他行为。)

我不完全确定这是否有用,因为当你说你试图实现LIKE时,我不明白你的意思。如果我完全误解了,请告诉我,我会删除这个答案:)


33
投票

我有同样的问题。

目前,我已经确定了基于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);

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


16
投票

更新:在EF 6.2中有一个类似的运算符

Where(i => DbFunctions.Like(searchstring ,like expression)

14
投票

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

它作为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


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'


1
投票

对于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
投票

re:“我们希望能够匹配blah blah foobar foo?bar?foo * bar?和其他复杂模式。”我还没有尝试过这个(还不需要),但你尝试过使用System.Text.RegularExpressions.RegEx吗?

© www.soinside.com 2019 - 2024. All rights reserved.