2014-11-07 8 views
2

출력을 반환하기 전에 필터링을 수행해야하는 보고서 집합이 있습니다. 다른 리포지토리에서 동일한 코드를 복제하지 않으려면 단일 익명 메서드로이 작업을 수행하고 싶습니다. Entity Framework를 사용하여 모델 유형이 모두 데이터베이스와 관련이 있고 기본 클래스 인 ReportBase에서 상속됩니다.익명 형식의 필터링 된 IQueryable <T>을 반환합니다.

이것은 현재 필터링을 구현하는 방법이며, 각 보고서 유형은이 컨텍스트를 사용하여이 메서드를 구현하고 다른 IQueryable 형식을 반환해야합니다.

private IQueryable<ReviewAgreement> GetFiltered(ReportFilter filter) 
{ 
    IQueryable<ReviewAgreement> reviewAgreementQueryable = Context.ReviewAgreements.Where(p => p.ClientWorkflowId == filter.ClientWorkflowId); 
    if (filter.AppraisalLevelId.HasValue) 
    { 
     reviewAgreementQueryable = reviewAgreementQueryable.Where(p => p.AppraisalLevelId == filter.AppraisalLevelId.Value); 
    } 
    return reviewAgreementQueryable; 
} 

저는 비 익숙한 방식으로이 비 기능적 예제에서와 같이 재사용 할 수 있도록 구현했습니다.

public IQueryable<T> GetFiltered(ReportFilter filter) 
{ 
    IQueryable<T> reportQueryable = Context.Set<T>(); 
    reportQueryable = reportQueryable.Where(p => p.ClientWorkflowId == filter.ClientWorkflowId); 

    if (filter.AppraisalLevelId.HasValue) 
    { 
     reportQueryable = reportQueryable.Where(p => p.AppraisalLevelId == filter.AppraisalLevelId.Value); 
    } 

    return reportQueryable; 
} 

오전 데 문제

Where의 사용이 모호 물론입니다, 그래서 p.ClientWorkflowId를 확인할 수 없습니다.

Func<T, TResult> 대리자를 사용하여 필터링 옵션을 전달하려고 시도했지만 Where 작업이 목록을 반환하려고하는 것 같습니다.

원하는 효과를 내기 위해 실제로 사용할 수있는 방법이 있습니까?

+0

'ClientWorkflowId'는 어디에 정의되어 있습니까? 'ReportBase'에 정의되어 있습니까, 아니면 여러분의 엔티티가 모두'ClientWorkflowId'를 따로 정의합니까? 'AppraisalLevelId'는 어떻습니까? 또한, 'Query >'을 기대하기 때문에 'Func '으로 필터를 전달하려는 시도가 실패했을 수 있습니다. –

+0

내가 넣은 예제는 많은 특성으로 실제로 필터링하고있는 다듬어 진 버전입니다. 하지만 그렇습니다. 모두 ReportBase에서 정의됩니다. 다른 모든 모델이 상속하는 모델. – Amicable

답변

5
  1. 이 작업을 수행하는 데 필요한 두 개의 ID 속성이있는 인터페이스를 선언하십시오.
  2. 엔티티에서 해당 인터페이스를 구현하는지 확인하십시오.
  3. 해당 인터페이스를 구현하는 일반 인수에 제약 조건을 추가하십시오.

    public IQueryable<T> GetFiltered<T>(ReportFilter filter) where T : ReportBase 
    { 
        // body unchanged 
    } 
    

    경우 : 기본 클래스 문제의 특성을 모두 정의하는 경우 다음 인터페이스를 필요로하지 않으며, 단순히 기본 클래스의 유형을 제한 할 수 있음을

주 당신은 이러한 속성을 나타 내기 위해 매개 변수를 받아들이는 경로를 따라가는 것이 가능합니다. 첫 번째로 쿼리 제공자가 객체를 분석 할 수 있도록 Func 객체가 아닌 표현식을 허용해야합니다. 그것은 일반 명 규모보다 우리가 표현에 대한 좀 더 관여가의 ID로 값을 비교 조건으로 이러한 선택기를 설정하는,

public IQueryable<T> GetFiltered<T>(ReportFilter filter, 
    Expression<Func<T, int>> clientIdSelector, 
    Expression<Func<T, int>> appraisalIdSelector) 
{ 

다음 :이에 함수 서명을 변경하는 것을 의미한다. 여기에 실제로 필요한 것은 Compose 메소드입니다. 위임자에게는 첫 번째 메소드의 결과 인 매개 변수로 호출 한 다른 메소드로 하나의 메소드를 작성하는 것은 충분히 간단합니다. 표현식이란 하나의 표현식의 본문을 가져 와서 매개 변수의 모든 인스턴스를 다른 표현식으로 대체 한 다음 모든 것을 새로운 람다로 묶는 것을 의미합니다.

public static Expression<Func<TFirstParam, TResult>> 
    Compose<TFirstParam, TIntermediate, TResult>(
    this Expression<Func<TFirstParam, TIntermediate>> first, 
    Expression<Func<TIntermediate, TResult>> second) 
{ 
    var param = Expression.Parameter(typeof(TFirstParam), "param"); 

    var newFirst = first.Body.Replace(first.Parameters[0], param); 
    var newSecond = second.Body.Replace(second.Parameters[0], newFirst); 

    return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param); 
} 

이 자체는 한 표현식의 모든 인스턴스를 다른 것으로 바꾸는 기능에 따라 달라집니다.

IQueryable<T> reportQueryable = Context.Set<T>(); 
reportQueryable = reportQueryable 
    .Where(clientIdSelector.Compose(id => id == filter.ClientWorkflowId)); 

if (filter.AppraisalLevelId.HasValue) 
{ 
    reportQueryable = reportQueryable 
     .Where(clientIdSelector.Compose(id => id == filter.AppraisalLevelId.Value)); 
} 

return reportQueryable; 
: 우리가 실제로 필터의 ID 값을 비교하여 우리의 선택기를 구성 할 수 장소에 이제 우리는이 모든 것을 가지고

public static Expression Replace(this Expression expression, 
    Expression searchEx, Expression replaceEx) 
{ 
    return new ReplaceVisitor(searchEx, replaceEx).Visit(expression); 
} 
internal class ReplaceVisitor : ExpressionVisitor 
{ 
    private readonly Expression from, to; 
    public ReplaceVisitor(Expression from, Expression to) 
    { 
     this.from = from; 
     this.to = to; 
    } 
    public override Expression Visit(Expression node) 
    { 
     return node == from ? to : base.Visit(node); 
    } 
} 

: 우리는 다음을 사용해야합니다 것을해야 할 일

+0

그는 단지'where T : ReportBase'를 정의 할 수 없습니까? 그의 일반적인'GetFiltered' 메소드에서 모든'ReportBase' 상속 된 보고서는이 2 개의 ID 속성을 상속 받았다고 가정합니다. – Michael

+0

@michaelmoore 그게 사실인지는 확실치 않았지만, OP가 내 의견에 따라이를 확인했다. 예, OP는 Servy의 답변에서 # 1과 # 2를 건너 뛰고 'where : T : ReportBase'를 추가하여 # 3으로 바로 갈 수 있습니다. –

+1

@michaelmoore 기본 클래스에서 두 속성을 모두 정의하는 경우 나는 편집에서 그것을 분명히했다. – Servy