2016-10-06 12 views
0

필터를 통과하는 표가 있습니다.모눈 속성에 엔터티 속성이없는 경우 모눈 필터링을 기반으로하는 LINQ 조건부/dynamic.LINQ 쿼리를 어떻게 만듭니 까?

var filter = new Filter(){ 
    Member = "Titles", 
    Operator = Filter.Operators.IsEqualTo, 
    Value = "Developer" 
}; 

가 그럼 난, 그래서 그렇게이 걸릴을 IQueryable을 확장 할 필요가 나는이 필터를 적용 할 dynamic.LINQ를 사용하는 방법이 있습니다 :

private IQueryable<TReportClass> ApplyFilter(ReportFilter filter, IQueryable<TReportClass> baseQuery) 
    { 
     switch (filter.Operator) 
     { 
      case ReportFilter.Operators.Contains: 
       baseQuery = baseQuery.Where(string.Format("{0}.Contains(@0)", filter.Member), filter.Value); 
       break; 
      case ReportFilter.Operators.DoesNotContain: 
       baseQuery = baseQuery.Where(string.Format("!{0}.Contains(@0)", filter.Member), filter.Value); 
       break; 
      case ReportFilter.Operators.IsEqualTo: 
       baseQuery = baseQuery.Where(string.Format("{0} = @0", filter.Member), filter.Value); 
       break; 
      case ReportFilter.Operators.IsNotEqualTo: 
       baseQuery = baseQuery.Where(string.Format("{0} != @0", filter.Member), filter.Value); 
       break; 
      case ReportFilter.Operators.StartsWith: 
       baseQuery = baseQuery.Where(string.Format("{0}.StartsWith(@0)", filter.Member), filter.Value); 
       break; 
      case ReportFilter.Operators.EndsWith: 
       baseQuery = baseQuery.Where(string.Format("{0}.EndsWith(@0)", filter.Member), filter.Value); 
       break; 
      case ReportFilter.Operators.IsNull: 
       baseQuery = baseQuery.Where(string.Format("{0} = NULL", filter.Member)); 
       break; 
      case ReportFilter.Operators.IsNotNull: 
       baseQuery = baseQuery.Where(string.Format("{0} != NULL", filter.Member)); 
       break; 
      case ReportFilter.Operators.IsEmpty: 
       baseQuery = baseQuery.Where(string.Format("string.IsNullOrEmpty({0})", filter.Member)); 
       break; 
      case ReportFilter.Operators.IsNotEmpty: 
       baseQuery = baseQuery.Where(string.Format("!string.IsNullOrEmpty({0})", filter.Member)); 
       break; 
     } 

     return baseQuery; 
    } 
을 그래서 같은 목적을 가질 수있다

그러나 이것은 비 콜렉션에서만 작동합니다. 컬렉션으로 작업하려면 어떻게해야합니까?

public class UserReport : Entity 
{ 
    public string Name { get; set; } 
    public string Email { get; set; } 
    public List<string> Titles { get; set; } 
} 

그리고 도심이 쿼리 : 나는이 모델이있는 경우

IQueryable<UserReport> filteredQuery = ApplyFilters(filters, baseQuery); 

이 어떻게로 변환하는 위의 필터를 변환 않습니다

IQueryable<UserReport> baseQuery = MyDbContext.DbSet<User>.Select(user => new UserReport 
     { 
      Id = user.Id, 
      Name = user.FirstName + " " + user.LastName, 
      Email = user.Email, 
      Titles = user.Positions.Select(apptment => apptment.Title).ToList() 
     }) 

을 같은 그래서 나는 호출 할 수 있습니다 LINQ는 다음과 같습니다.

baseQuery.Where(userReport => userReport.Titles.Any(title => title == "Developer") 

Can tha 동적 LINQ로 끝나야합니까? 아니면 내 자신의 술어를 만들어야합니까? 그렇다면 어떻게해야합니까?

답변

1

System.Linq.DynamicSystem.Linq.Expressions 모두 가능합니다.

System.Linq.Dynamic을 사용하여 현재 코드에 가까운 솔루션을 제공합니다. 당신이 필요로하는 회원이 수집하는 경우 확인하고 동적 기준에 대해 다음 패턴을 사용하는 것입니다

컬렉션 :{PropertyName}.Any(it{condition})
대상 :{PropertyName}{condition}

그리고 구현은 기본적으로 (이 같은 수) Func<string, string, string>string.Format 교체 :

private IQueryable<TReportClass> ApplyFilter(ReportFilter filter, IQueryable<TReportClass> baseQuery) 
{ 
    var property = typeof(TReportClass).GetProperty(filterMember); 
    bool isCollection = property.Type != typeof(string) && 
     && typeof(IEnumerable).IsAssignableFrom(property.Type); 
    Func<string, string, string> condtion; 
    if (isCollection) 
     condition = (format, member) => string.Format("{0}.Any({1})", member, string.Format(format, "it")); 
    else 
     condition = (format, member) => string.Format(format, member); 
    switch (filter.Operator) 
    { 
     case ReportFilter.Operators.Contains: 
      baseQuery = baseQuery.Where(condition("{0}.Contains(@0)", filter.Member), filter.Value); 
      break; 
     case ReportFilter.Operators.DoesNotContain: 
      baseQuery = baseQuery.Where(condition("!{0}.Contains(@0)", filter.Member), filter.Value); 
      break; 
     case ReportFilter.Operators.IsEqualTo: 
      baseQuery = baseQuery.Where(condition("{0} = @0", filter.Member), filter.Value); 
      break; 
     case ReportFilter.Operators.IsNotEqualTo: 
      baseQuery = baseQuery.Where(condition("{0} != @0", filter.Member), filter.Value); 
      break; 
     case ReportFilter.Operators.StartsWith: 
      baseQuery = baseQuery.Where(condition("{0}.StartsWith(@0)", filter.Member), filter.Value); 
      break; 
     case ReportFilter.Operators.EndsWith: 
      baseQuery = baseQuery.Where(condition("{0}.EndsWith(@0)", filter.Member), filter.Value); 
      break; 
     case ReportFilter.Operators.IsNull: 
      baseQuery = baseQuery.Where(condition("{0} = NULL", filter.Member)); 
      break; 
     case ReportFilter.Operators.IsNotNull: 
      baseQuery = baseQuery.Where(condition("{0} != NULL", filter.Member)); 
      break; 
     case ReportFilter.Operators.IsEmpty: 
      baseQuery = baseQuery.Where(condition("string.IsNullOrEmpty({0})", filter.Member)); 
      break; 
     case ReportFilter.Operators.IsNotEmpty: 
      baseQuery = baseQuery.Where(condition("!string.IsNullOrEmpty({0})", filter.Member)); 
      break; 
    } 

    return baseQuery; 
} 
+0

감사합니다, 그것은 한 번만 사용 반사 더 성능이 좋은 것 outsi de의 ApplyFilter를 호출하고 각 ApplyFilter 호출에서 반사를 사용하지 않고 ApplyFilter에 모든 컬렉션 속성 이름을 전달 하시겠습니까? 또한 속성이'typeof (string)'이 아니고'typeof (IEnumerable) '에서 할당 할 수 있는지를 처음으로 확인하는 이유에 대해 궁금합니다. 'typeof (IEnumerable ) .IsAssignableFrom (property.Type)'을 검사하는 문장을 하나만 가질 수는 없습니까? – SventoryMang

+0

(1)이 경우 반영의 영향은 무시할 수 있습니다. 결과 집합의 각 요소마다 실행되는 것이 아닙니다. 또한 System.Linq.Expressions 클래스를 사용하여 표현식 트리를 작성하면 어쨌든 많은 반향이 발생합니다. 어떻게 동적 LINQ가 전달 된 문자열을 처리한다고 생각합니까? (2)'string'리스트 만 지원하면 충분합니다. 위의 내용은 더 일반적이며 값이있는 모든 목록에서 작동합니다. 물론 필터 연산자 중 일부는 문자열에 대해서만 정의되지만,'Is (Not) EqualTo','Is (Not) Null'과 같은 것들은'int','DateTime' 등에 대해서 사용될 수 있습니다. –

+0

어쨌든, 위의 샘플 구현은 당신이 필요에 맞게 조정할 수 있습니다 :) –