/// <summary> /// BinaryExpressionの子を走査します。 /// </summary> /// <param name="node">走査する式</param> /// <returns>式またはいずれかの部分式が変更された場合は変更された式。それ以外の場合は元の式。</returns> protected override Expression VisitBinary(BinaryExpression node) { //--- AND/OR : 左右を保持する要素として生成 //--- 比較演算子 (<, <=, >=, >, ==, !=) : 左辺のプロパティ名と右辺の値を抽出 switch (node.NodeType) { case ExpressionType.AndAlso: case ExpressionType.OrElse: { var element = new PredicateElement(node.NodeType.ToPredicateOperator()); return(this.VisitCore(element, () => base.VisitBinary(node))); } case ExpressionType.LessThan: case ExpressionType.LessThanOrEqual: case ExpressionType.GreaterThan: case ExpressionType.GreaterThanOrEqual: case ExpressionType.Equal: case ExpressionType.NotEqual: { var element = this.ParseBinary(node); return(this.VisitCore(element, () => base.VisitBinary(node))); } } return(base.VisitBinary(node)); }
/// <summary> /// UnaryExpressionの子を走査します。 /// </summary> /// <param name="node">走査する式。</param> /// <returns>式またはいずれかの部分式が変更された場合は変更された式。それ以外の場合は元の式。</returns> protected override Expression VisitUnary(UnaryExpression node) { //--- !x.CanPlay の形式は xCanPlay != true として扱う if (node.NodeType == ExpressionType.Not) { if (this.IsBooleanProperty(node.Operand as MemberExpression)) { var element = new PredicateElement(PredicateOperator.NotEqual); return(this.VisitCore(element, () => base.VisitUnary(node))); } } return(base.VisitUnary(node)); }
/// <summary> /// 指定された条件式要素自身とその子孫要素を取得します。 /// </summary> /// <param name="element">条件式要素</param> /// <returns>条件式要素のコレクション</returns> public static IEnumerable <PredicateElement> DescendantsAndSelf(this PredicateElement element) { if (element == null) { throw new ArgumentNullException(nameof(element)); } yield return(element); foreach (var x in element.Descendants()) { yield return(x); } }
/// <summary> /// 指定された条件式要素の子要素を取得します。 /// </summary> /// <param name="element">条件式要素</param> /// <returns>条件式要素のコレクション</returns> public static IEnumerable <PredicateElement> Children(this PredicateElement element) { if (element == null) { throw new ArgumentNullException(nameof(element)); } if (element.HasChildren) { yield return(element.Left); yield return(element.Right); } }
/// <summary> /// 指定された条件式要素の子孫要素を取得します。 /// </summary> /// <param name="element">条件式要素</param> /// <returns>条件式要素のコレクション</returns> public static IEnumerable <PredicateElement> Descendants(this PredicateElement element) { if (element == null) { throw new ArgumentNullException(nameof(element)); } foreach (var child in element.Children()) { yield return(child); foreach (var grandChild in child.Descendants()) { yield return(grandChild); } } }
/// <summary> /// MethodCallExpressionの子を走査します。 /// </summary> /// <param name="node">走査する式</param> /// <returns>式またはいずれかの部分式が変更された場合は変更された式。それ以外の場合は元の式。</returns> protected override Expression VisitMethodCall(MethodCallExpression node) { //--- Enumerable.Contains if (node.Method.DeclaringType == typeof(Enumerable)) { if (node.Method.Name == nameof(Enumerable.Contains)) { //--- プロパティ名を取得 var propertyName = this.ExtractMemberName(node.Arguments[1]); if (propertyName == null) { throw new InvalidOperationException(); } //--- 要素生成 //--- in句は1000件以上あるとエラーが発生するためorでつなぐ var source = (this.ExtractValue(node.Arguments[0]) as IEnumerable) .Cast <object>() .Buffer(1000) .Select(x => x.ToArray()); PredicateElement root = null; foreach (var x in source) { if (root != null) { var parent = new PredicateElement(PredicateOperator.OrElse); parent.Left = new PredicateElement(PredicateOperator.Contains, this.Parameter.Type, propertyName, x); parent.Right = root; root = parent; continue; } root = new PredicateElement(PredicateOperator.Contains, this.Parameter.Type, propertyName, x); } return(this.VisitCore(root, () => base.VisitMethodCall(node))); } } //--- default return(base.VisitMethodCall(node)); }
/// <summary> /// MemberExpressionの子を走査します。 /// </summary> /// <param name="node">走査する式</param> /// <returns>式またはいずれかの部分式が変更された場合は変更された式。それ以外の場合は元の式。</returns> protected override Expression VisitMember(MemberExpression node) { //--- 「x => x.CanPlay == true」ではなく「x => x.CanPlay」のような書き方への対応 if (this.IsBooleanProperty(node as MemberExpression)) { //--- 親要素がない var info = (PropertyInfo)node.Member; var parent = this.Cache.Count == 0 ? null : this.Cache.Peek(); if (parent == null) { var element = new PredicateElement(PredicateOperator.Equal, info.PropertyType, info.Name, true); return(this.VisitCore(element, () => base.VisitMember(node))); } switch (parent.Operator) { //--- && か || の場合は左辺/右辺のどちらか case PredicateOperator.AndAlso: case PredicateOperator.OrElse: { var element = new PredicateElement(PredicateOperator.Equal, info.PropertyType, info.Name, true); return(this.VisitCore(element, () => base.VisitMember(node))); } //--- == / != / !x.CanPlay の場合 case PredicateOperator.Equal: case PredicateOperator.NotEqual: if (parent.PropertyName == null) { parent.Type = info.PropertyType; parent.PropertyName = info.Name; parent.Value = true; } break; } } return(base.VisitMember(node)); }
/// <summary> /// 式を走査します。 /// </summary> /// <param name="element">要素生成</param> /// <param name="baseCall">基底メソッド呼び出しデリゲート</param> /// <returns>式またはいずれかの部分式が変更された場合は変更された式。それ以外の場合は元の式。</returns> private Expression VisitCore(PredicateElement element, Func <Expression> baseCall) { //--- 親要素と関連付け var parent = this.Cache.Count == 0 ? null : this.Cache.Peek(); if (parent != null) { if (parent.Left == null) { parent.Left = element; } else if (parent.Right == null) { parent.Right = element; } else { throw new InvalidOperationException(); } } //--- 子要素を解析 this.Cache.Push(element); var result = baseCall(); this.Cache.Pop(); //--- キャッシュがなくなった場合 (= Root) if (this.Cache.Count == 0) { this.Root = element; } //--- ok return(result); }