2017-04-19 9 views
0

나는 상당한 시간 동안 일반 쿼리를 생성하기 위해 LinqKit을 사용 해왔다.Linqkit의 PredicateBuilder를 사용하는 일반 쿼리

항상 저를 괴롭 히고있는 한 가지 사실은 필터에서 전송 된 값이 유효한지 항상 테스트해야한다는 것입니다.

예 : 문자열 필터가 있다고 가정합니다. 조건은 Equal, StartsWith, EndsWith 및 Contains가 될 수 있습니다.

내 방법은 다음과 같이 보일 것입니다 :

public List<MyModel> Get(MyModelFilter filter) 
{ 
    if (string.IsNullOrEmpty(filter.prop)) 
    { 
     predicate = predicate.And(_myModel => myModel.Prop.Contains(filter.prop)); 
    } 

    // Plus a giant amount of if's with multiple filters 

    return DbSet.AsExpandable() 
      .Where(predicate) 
      .ToList(); 
} 

는 만약의이 무리를 종료하려면, 나는 속성에 필터를 적용하는 일반적인 방법을 만들하기로 결정했다. 내 생각은 표현의 생성 논리를 필터가 적용됩니다 재산, 필터 정의를 통과하고 캡슐화하는 것입니다

이 유형의 어떤 것이 들어

public List<MyModel> Get(MyModelFilter filter) 
{ 
    predicate = predicate.And(_myModel => myModel.Prop, filter.PropFilterDefinition); 

    // Goodnye If's, Only others filter impl 

    return DbSet.AsExpandable() 
      .Where(predicate) 
      .ToList(); 
} 

, 내가 ' 문제는 필터가 적용되지 않으며, 대답은 바로이 호출에 있다는 것이다이

public static Expression<Func<TPredicate, bool>> And<TPredicate>(
    this ExpressionStarter<TPredicate> predicate, 
    Func<TPredicate, string> property, StringFilterDefinition filter, 
    bool ignoreNull = true) 
{ 
    if (InvalidStringFilter(filter, ignoreNull)) 
    { 
     return predicate; 
    } 

    // This is LinqKit's And Extension Method 
    return predicate.And(BuildPredicate(property, filter)); 
} 

private static Expression<Func<TPredicate, bool>> BuildPredicate<TPredicate>(
    Func<TPredicate, string> property, 
    StringFilterDefinition filter) 
{ 
    if (filter.Filter == StringFilterComparators.Equal) 
    { 
     return x => property.Invoke(x) == filter.Value; 
    } 

    if (filter.Filter == StringFilterComparators.BeginsWith) 
    { 
     return x => property.Invoke(x).StartsWith(filter.Value); 
    } 

    if (filter.Filter == StringFilterComparators.EndsWith) 
    { 
     return x => property.Invoke(x).EndsWith(filter.Value); 
    } 

    return x => property.Invoke(x).Contains(filter.Value); 
} 

private static bool InvalidStringFilter(
    StringFilterDefinition filter, 
    bool ignoreNullValue = true) 
{ 
    if (filter?.Filter == null) 
    { 
     return true; 
    } 

    return ignoreNullValue && string.IsNullOrEmpty(filter.Value); 
} 

을 처리하기 위해 몇 가지 확장 방법을 만들었습니다. EF는 위의 표현식을 SQL로 변환 할 수 없습니다. EF의 오차는

Microsoft.EntityFrameworkCore.Query.Internal.SqlServerQueryCompilationContextFactory [8] LINQ 식 '(__property_0.Invoke가 ([X]) == __filter_Value_1)' 번역 할 수 없습니다되고 현지에서 평가되었습니다. 이 경고를 구성하려면 DbContextOptionsBuilder.ConfigureWarnings API (이벤트 ID 'RelationalEventId.QueryClientEvaluationWarning')을 사용하십시오. DbContext.OnConfiguring 메서드를 재정의하거나 응용 프로그램 서비스 공급자에서 AddDbContext를 사용할 때 ConfigureWarnings을 사용할 수 있습니다.

질문 :

가 어떻게이 건설 공사를 할 수 ? 또한 가장 좋은 방법에 대한 제안 사항이 있으십니까? 당신은 것

답변

4

PredicateBuilder 외에 LINQKit AsExpandable, ExpandInvoke 사용자 정의 확장 메서드가 제공하는 정말 유용한 기능이 제대로 식 트리 내부표현을 포함 할 수있을 것을 잊고있다.

해당 기능을 사용하려면 Func<...> 대신 Expression<Func<...>>을 사용해야합니다. 게시 된 코드에서 Func<TPredicate, string>의 모든 항목을 Expression<Func<TPredicate, string>>으로 바꾸면 문제가 해결됩니다.