2016-07-21 10 views
1

私は、IQueryableにwhere述語として適用するDateRangeクラスを用意しています。開始日と終了日を自動的に使用し、自動的に開かれた間隔または閉じた間隔を使用します。1つの式を別の式に含めるにはどうすればよいですか?

public class DateRange 
{ 
    public DateTime? BeginDate { get; set; } 
    public DateTime? EndDate { get; set; } 

    public bool BeginInclusive { get; set; } 
    public bool EndInclusive { get; set; } 

    public DateRange() 
    { 
     BeginInclusive = true; 
     EndInclusive = false; 
    } 

    public IQueryable<T> Apply<T>(IQueryable<T> source, Expression<Func<T,DateTime>> dateField) 
    { 
     var result = source; 
     if (BeginDate.HasValue) 
     { 
      if (BeginInclusive) 
       result = result.Where(x => dateField >= BeginDate); //does not compile 
      else 
       result = result.Where(x => dateField > BeginDate); //does not compile 
     } 
     if (EndDate.HasValue) 
     { 
      if (EndInclusive) 
       result = result.Where(x => dateField <= EndDate); //does not compile 
      else 
       result = result.Where(x => dateField < EndDate); //does not compile 
     } 
     return result; 
    } 
} 

そして、私はこのようにそれを呼びたい、DateFieldには、だから私は適用法にメンバー式を渡したい、それが適切に適用する必要がありT.

DateRange d; 
IQueryable<T> q; 
q = d.Apply(q, x => x.DateField); 

の任意の日付と時刻のプロパティですWhere句を結果セットに追加しますが、に埋め込まれたdateFieldメンバー式を取得する方法を理解できません。ここで、述語の式です。上のクラスで "コンパイルしない"行を参照してください。私は何とかdateFieldを変換するか、または他の方法で述語式を構築する必要がありますが、どうやってそれを行うのか分かりません。

+1

あなたは 'のDateField> = BeginDate'' Expression'クラスのメソッドを使用して、工芸品を手にする必要があります:

public static Expression ReplaceParameter(this Expression expression, ParameterExpression toReplace, Expression newExpression) { return new ParameterReplaceVisitor(toReplace, newExpression) .Visit(expression); } public class ParameterReplaceVisitor : ExpressionVisitor { private ParameterExpression from; private Expression to; public ParameterReplaceVisitor(ParameterExpression from, Expression to) { this.from = from; this.to = to; } protected override Expression VisitParameter(ParameterExpression node) { return node == from ? to : base.Visit(node); } } 

これは、あなたのコードを書くことができます。 – MarcinJuraszek

+0

私はそれを行う方法がわかりません。 Expressionクラスそのものはあまり働かない。私はLinqKitのPredicateBuilderを使用しましたが、この状況ではあまり役に立ちません。最後の 'where'述語はLinq-to-Entitiesでも機能する必要があります。 – Triynko

+0

これはどのように見えますか: 'var expr = Expression.GreaterThanOrEqual(dateField.Body、Expression.Constant(BeginDate)); result = result.Where(Expression.Lambda >(expr)); ' – Triynko

答えて

0

あなたはExpressionクラスのメソッドを使用してdateField >= BeginDateクラフトを手に持っています。

(...) 
    if (BeginInclusive) 
    { 
     var greaterOrEqual = 
      Expression.Lambda<Func<T, bool>>(
       Expression.GreaterThanOrEqual(
        dateField.Body, 
        Expression.Constant(BeginDate)), 
       dateField.Parameters); 

     result = result.Where(greaterOrEqual); 
    } 
(...) 

他の場合も同様である。

+0

すごく、それは私が思いついたものです。私は最終的なコードで別の答えを投稿して、人々がそれを使うことができるようにするつもりです。私はLinq-to-Entitiesですべての作品が期待通りに動作することを確認するだけです。 – Triynko

+0

私は自分自身の学習に '' IEnumerable ''を使ってみようとしていますが、コンパイルできないのですが、何が見逃しているのでしょうか? "public IEnumerable を適用します(IEnumerable ソース、Func 、DateTime、bool> dateField) { var result = source; return result.Where(x => dateField(source、x)); } '' –

0

これを理解した後に作成された更新されたアプライメソッドです。

public IQueryable<T> Apply<T>(IQueryable<T> source, Expression<Func<T,DateTime>> dateField) 
    { 
     Expression predicate; 
     if (BeginDate.HasValue) 
     { 
      if (BeginInclusive) 
       predicate = Expression.GreaterThanOrEqual(dateField.Body, Expression.Constant(BeginDate, typeof(DateTime))); 
      else 
       predicate = Expression.GreaterThan(dateField.Body, Expression.Constant(BeginDate, typeof(DateTime))); 
      source = source.Where(Expression.Lambda<Func<T, bool>>(predicate)); 
     } 
     if (EndDate.HasValue) 
     { 
      if (EndInclusive) 
       predicate = Expression.LessThanOrEqual(dateField.Body, Expression.Constant(EndDate, typeof(DateTime))); 
      else 
       predicate = Expression.LessThan(dateField.Body, Expression.Constant(EndDate, typeof(DateTime))); 
      source = source.Where(Expression.Lambda<Func<T, bool>>(predicate)); 
     } 
     return source; 
    } 

次のそれのように使用することができますので、私は、拡張メソッドに変換します:

DateRange range; 
IQueryable<T> q; 
q = q.WhereInDateRange(range, x => x.DateField); 
0

あなたがここに行うために探しているどのような表現を構成することです。別の式の結果に1つの式を適用しようとしています。

public static Expression<Func<TSource, TResult>> Compose<TSource, TIntermediate, TResult>(
    this Expression<Func<TSource, TIntermediate>> first, 
    Expression<Func<TIntermediate, TResult>> second) 
{ 
    var param = Expression.Parameter(typeof(TSource)); 
    var intermediateValue = first.Body.ReplaceParameter(first.Parameters[0], param); 
    var body = second.Body.ReplaceParameter(second.Parameters[0], intermediateValue); 
    return Expression.Lambda<Func<TSource, TResult>>(body, param); 
} 

これは、式のパラメータを式で置き換えるために次の方法を使用します。

public IQueryable<T> Apply<T>(IQueryable<T> source, 
    Expression<Func<T, DateTime>> dateField) 
{ 
    var result = source; 
    if (BeginDate.HasValue) 
    { 
     if (BeginInclusive) 
      result = result.Where(dateField.Compose(date => date >= BeginDate)); 
     else 
      result = result.Where(dateField.Compose(date => date > BeginDate)); 
    } 
    if (EndDate.HasValue) 
    { 
     if (EndInclusive) 
      result = result.Where(dateField.Compose(date => date <= EndDate)); 
     else 
      result = result.Where(dateField.Compose(date => date < EndDate)); 
    } 
    return result; 
} 
関連する問題