2011-08-15 4 views
8

다음과 같이 일부 문자열이 "일치"하는지 여부를 결정하는 특별한 방법이 있습니다.엔티티와 도우미 메서드에 LINQ를 사용하는 동안 DRY 상태를 유지하는 방법?

public bool stringsMatch(string searchFor, string searchIn) 
{ 
    if (string.IsNullOrEmpty(searchFor)) 
    { 
    return true; 
    } 

    return searchIn != null && 
    (searchIn.Trim().ToLower().StartsWith(searchFor.Trim().ToLower()) || 
    searchIn.Contains(" " + searchFor)); 
} 

Linq To Entities 및이 도우미를 사용하여 데이터베이스에서 일치 항목을 가져 오려고합니다. 그러나 내가 이것을 시도 할 때 :

IQueryable<Blah> blahs = query.Where(b => stringsMatch(searchText, b.Name); 

"엔티티에 엔티티가 인식하지 못한다 ..."

다음과 같이 코드를 다시 작성하면 :

IQueryable<Blah> blahs = query.Where(b => 
     string.IsNullOrEmpty(searchText) || 
     (b.Name != null && 
     (b.Name.Trim().ToLower().StartsWith(searchText.Trim().ToLower()) || 
     b.Name.Contains(" " + searchText))); 

논리적으로 동등하다면 문제는 해결됩니다. 문제는 코드가 읽기 쉽지 않기 때문에 일치시키려는 다른 엔티티마다 코드를 다시 써야한다는 것입니다.

this one과 같은 질문에서 말할 수있는 한, 지금은 할 수없는 일은 불가능합니다. 그러나 나는 뭔가를 놓치기를 바라고 있습니까?

+0

try [Predicate Builder] (http://www.albahari.com/nutshell/predicatebuilder.aspx) – Eranga

답변

4

자유롭게 사용할 수있는 라이브러리 LINQKit (@Eranga에서 언급 한대로)을 사용하면이 작업이 합리적이됩니다. 내가 지금 가지고있는 코드를 LINQKit 사용하면 다음과 같습니다

protected Expression<Func<T, bool>> stringsMatch(string searchFor, Expression<Func<T, string>> searchIn) 
{ 
    if (string.IsNullOrEmpty(searchFor)) 
    { 
    return e => true; 
    } 

    return 
    e => 
    (searchIn.Invoke(e) != null && 
     (searchIn.Invoke(e).Trim().ToLower().StartsWith(searchFor.Trim().ToLower()) || 
     searchIn.Invoke(e).Contains(" " + searchFor))); 
} 

그리고합니다 (AsExpandable() 호출을주의)

IQueryable<Blah> blahs = query().AsExpandable().Where(StringsMatch(searchText, b => b.Name)); 

마법 부분이 찾아서 있습니다 이런 식으로 호출 할 필요가있다.Invoke (e) 호출과 AsExpandable()을 사용하여 작업을 수행 할 수있는 래퍼 계층을 추가합니다.

AsExpandable() 비트는 원본 작성자 에 의해 자세히 설명됩니다.

표현의 세부 사항에 약간의 불투명 함을 남기 때문에, 더 좋게/더 짧게/더 명확하게 표현할 수 있다면이 답변을 편집하십시오.

5

필터링 할 'blahs'(클래스)가 모두 동일한 구조 인 경우 이와 같은 간단한 방법을 사용할 수 있습니다. 가장 큰 차이점은 Linq가 구문 분석 할 수 있어야하는 표현식을 반환하고 전체 인스턴스를 가져오고 문자열 이름을 가져 오는 대신 이름에 필터를 적용한다는 것입니다.

public static Expression<Func<T, bool>> BuildStringMatch<T>(string searchFor) where T : IHasName 
    { 
     return b => 
       string.IsNullOrEmpty(searchFor) || 
       (b.Name != null && 
       (b.Name.Trim().ToLower().StartsWith(searchFor.Trim().ToLower()) || 
       b.Name.Contains(" " + searchFor))); 
    } 

는이 같은이 방법을 사용할 수 있습니다 :

당신은 필터링과 같은 몇 가지 인터페이스를 구현 싶어 모든 클래스 가정
IQueryable<Blah> blahs = query.Where(BuildStringMatch<Blah>(searchText)); 

:

public interface IHasName 
    { 
     string Name { get; } 
    } 

당신이 만약을 다른 속성에서 필터링하고 싶다면 이렇게하는 간단한 코드로는 할 수없는 것이라고 생각합니다. 표현식을 리플렉션 (또는 리플렉션을 사용하는 라이브러리의 도움)으로 직접 작성해야한다고 생각합니다. 여전히 가능하지만 훨씬 어렵습니다.

편집 : 동적 행동을해야 할 것 같은데, 그래서 나는 this questiondtb '의 대답에서 일부 논리를 빌려이 함께했다 :

 IQueryable<Blah> blahs2 = query.Where(BuildStringMatch<Blah>(b => b.Name, searchText)); 
:

public static Expression<Func<T, bool>> BuildStringMatch<T>(Expression<Func<T, string>> property, string searchFor) 
{ 
    var searchForExpression = Expression.Constant(searchFor, typeof(string)); 
    return 
     Expression.Lambda<Func<T, bool>>(
      Expression.OrElse(
       Expression.Call(typeof(string), "IsNullOrEmpty", null, searchForExpression), 
       Expression.AndAlso(
        Expression.NotEqual(property.Body, Expression.Constant(null, typeof(string))), 
        Expression.OrElse(
         Expression.Call(Expression.Call(Expression.Call(property.Body, "Trim", null), "ToLower", null), "StartsWith", null, 
          Expression.Call(Expression.Call(searchForExpression, "Trim", null), "ToLower", null)), 
         Expression.Call(property.Body, "Contains", null, Expression.Call(typeof(string), "Concat", null, Expression.Constant(" "), searchForExpression)) 
        ) 
       ) 
      ), 
      property.Parameters 
     ); 
} 

당신은 그것을 좋아 사용합니다

길고 자세한 내용이지만 C# 코드에서 작성된 원래 메서드와 어떻게 비슷한지 확인할 수 있습니다. 참고 :이 코드는 테스트하지 않았으므로 몇 가지 작은 문제가있을 수 있습니다.하지만 이는 일반적인 아이디어입니다.

+0

응답 해 주셔서 감사합니다. 이런 식으로 생각했지만 유연함이 부족합니다. 일치시키고 자하는 것을 "이름"이라고 부름). 더 복잡한 방법으로 시작해야 할 힌트가 있습니까? – Dan

+0

몇 가지 샘플 코드를 포함하도록 답을 편집했습니다. 그것은 확실히 더 복잡하게 보입니다.하지만 한 번만 쓰면됩니다. 아마도 그렇게 나쁘지 않을 것입니다. –

+0

고마워, 그게 정말 도움이된다. (나는 한 번 upvote 만 할 수있다.) 그러나 코드베이스에서 LINQKit이라는 라이브러리를 사용하여 매우 비슷한 방식으로 뭔가 다른 것을 수행하는 코드를 발견했습니다. 전체 세부 정보와 함께 새로운 답변을 추가하겠습니다. – Dan

관련 문제