Entity Framework의 연산자처럼?


92

문자열 필드가있는 엔터티에 대해 Entity Framework에서 "LIKE"연산자를 구현하려고하지만 지원되지 않는 것 같습니다. 다른 사람이 이와 같은 작업을 시도한 적이 있습니까?

블로그 게시물 은 우리가 겪고있는 문제를 요약합니다. contains를 사용할 수 있지만 LIKE의 가장 사소한 경우에만 일치합니다. contains, startswith, endswith 및 indexof를 결합하면 표준 와일드 카드와 Linq를 엔티티 코드로 변환해야합니다.


1
이미 EF 6.2.x를 사용중인 경우이 답변으로 이동하십시오 . 에 이 답변 당신은 EF 코어 2.X를 사용하는 경우
CodeNotFound

답변:


36

이것은 현재 오래된 게시물이지만 답변을 찾는 사람에게는 이 링크 가 도움이 될 것입니다. 이미 EF 6.2.x를 사용중인 경우이 답변으로 이동하십시오 . 에 이 답변 당신은 EF 코어 2.X를 사용하는 경우

짧은 버전 :

SqlFunctions.PatIndex 메서드-모든 유효한 텍스트 및 문자 데이터 형식에서 지정된 식에서 패턴이 처음 나타나는 시작 위치를 반환하거나 패턴이없는 경우 0을 반환합니다.

네임 스페이스 : System.Data.Objects.SqlClient 어셈블리 : System.Data.Entity (System.Data.Entity.dll에 있음)

포럼 스레드 에도 약간의 설명이 표시됩니다 .


59
이 질문으로 다시 연결되는 MSDN 포럼으로 연결되는 답변이 아래 답변에 어떻게 적용됩니까?
Eonasdan

대답은 SqlFunctions.PatIndex 메서드를 사용하는 것이 었습니다. 링크 된 포럼 스레드는 좀 더 "배경"정보를 제공하는 것이 었습니다.
Yann Duran

아래의 대답은 간단한 패턴에 적합하지만 "WHERE Name LIKE 'abc [0-9] %'"또는 다른 더 복잡한 패턴을 말하고 싶다면, 단순히 Contains ()를 사용하는 것으로는 잘리지 않습니다.
HotN 2014

1
이 질문에 대한 이전 답변의 복제 . (첫 부분이 아니라 대체 솔루션입니다.)
Frédéric

154

EF에 대해 실제로는 모르지만 LINQ to SQL에서는 일반적으로 String.Contains를 사용하여 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 2009-06-23

2
내가 아는 것은 아닙니다. 복잡한 패턴은 결국 db에 특화되고 일반적인 방식으로 표현하기가 어렵다고 생각합니다.
Jon Skeet

4
@Jon Skeet : 내가 아는 한 LIKE 기능은 ANSI 표준에 있으며 SQL Server, Oracle 및 DB2에서 거의 동일합니다.
AK

2
이 연산자와 MS SQL을 사용하면서 본 한 가지는 EF가이를 이스케이프 된 매개 변수로 추가한다는 것입니다. "Name LIKE @ p__linq__1 ESCAPE N ''~ ''"이것은 매우 제한된 사용 사례에서 검색 문자열이 검색 문자열보다 훨씬 느리게 수행됩니다. "Name like '% xyz %'라는 쿼리에 있습니다. 시나리오의 경우 여전히 StartsWith 및 Contains를 사용하고 있지만 동적 linq를 통해 수행합니다. 그 이유는 내 시나리오에서 생성되는 SQL 문에 매개 변수를 주입하기 때문입니다. .보다 효율적인 쿼리 확실하지 이것이 EF 4.0 일이나하지 않을 경우 당신은 또한 같은 일 ... 달성하기 위해 ObjectQueryParameters를 사용할 수 있습니다.
셰인 누빌

35

나는 같은 문제가 있었다.

지금은 http://www.codeproject.com/Articles/11556/Converting-Wildcards-to-Regexes?msg=1423024#xx1423024xx를 기반으로 클라이언트 측 Wildcard / Regex 필터링을 사용했습니다 . 간단하고 다음과 같이 작동합니다. 예상됩니다.

이 주제에 대한 또 다른 토론을 찾았습니다. 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);

참고 :이 솔루션은 비표준 PATINDEX 함수를 사용하기 때문에 SQL-Server 전용입니다.


PatIndex가 "작동"하는 동안 다시 사용자에게 물릴 것입니다. where 절의 PatIndex는 필터링하려는 열의 인덱스를 사용하지 않습니다.
BlackICE 2014 년

@BlackICE 이것은 예상됩니다. 내부 텍스트 (% CD % BLUE %)를 검색 할 때 서버는 인덱스를 사용할 수 없습니다. 가능하면 처음부터 텍스트를 검색하는 것이 더 효율적입니다 (CD % BLUE %).
surfen 2014-01-23

@surfen patindex는 그보다 나쁘지만 %가 앞에 없어도 색인을 사용하지 않으며 patindex로 (BLUE CD %)를 검색하면 열 색인을 사용하지 않습니다.
BlackICE 2014 년

23

업데이트 : EF 6.2에는 like 연산자가 있습니다.

Where(obj => DbFunctions.Like(obj.Column , "%expression%")

이것은 더 명확한 예가 Where(obj => DbFunctions.Like(obj.Column , "%expression%")아닐까요?
DCD

그것은 확실하다. 변경
Lode Vlaeminck

20

거기에 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것보다 실제로 번역되었습니다 .CHARINDEXContains


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


1
앞으로 EF에서 멀어지고 싶다면 Entity SQL에서 벗어나고 싶을 것입니다. 안전하게 플레이하고 대신 원래 응답의 Contains (), StartsWith () 및 EndsWith () 옵션을 사용하십시오.
Stephen Newman

1
그것은 잘 컴파일되지만 런타임에 실패합니다.
brien 2009-06-23

게시 한 코드가 런타임에 실패합니까? Microsoft 링크에서 제공됩니다.
Robert Harvey

나는 우리가 가진 것과 동일한 문제를 설명하는 블로그 게시물에 대한 링크로 질문을 편집했습니다.
brien 2009-06-23

Contains ()가 티켓 인 것 같습니다. 그러나 Jon Skeet이 지적했듯이 포함이 요구 사항을 충족하지 않는 경우 데이터베이스를 직접 조작하는 실제 SQL로 드롭 다운해야 할 수 있습니다.
Robert Harvey

2

MS Sql을 사용하는 경우 와일드 카드 검색을 위해 % 문자를 지원하는 두 가지 확장 메서드를 작성했습니다. (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, 내가 도울 수있는 것이 있습니까? 오류 메시지는 무엇입니까?
스티븐 총

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

Link to Entities에서 실제와 같은 것을 아주 쉽게 사용할 수 있습니다.

더하다

    <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

<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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.