Permalink
Browse files

bool型のプロパティを単項式として書けるようにした

  • Loading branch information...
1 parent 135db65 commit 83442316714bb87b9aee508dd5ba5615b84d6c61 Takaaki Suzuki committed Mar 19, 2016
@@ -20,19 +20,19 @@ internal sealed class PredicateElement
/// <summary>
/// 左辺のインスタンスの型を取得します。
/// </summary>
- public Type Type { get; }
+ public Type Type { get; internal set; }
/// <summary>
/// 左辺のプロパティ名を取得します。
/// </summary>
- public string PropertyName { get; }
+ public string PropertyName { get; internal set; }
/// <summary>
/// 右辺の値を取得します。
/// </summary>
- public object Value { get; }
+ public object Value { get; internal set; }
/// <summary>
@@ -82,6 +82,51 @@ protected override Expression VisitBinary(BinaryExpression 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>
/// MethodCallExpressionの子を走査します。
/// </summary>
/// <param name="node">走査する式</param>
@@ -123,6 +168,24 @@ protected override Expression VisitMethodCall(MethodCallExpression node)
//--- default
return base.VisitMethodCall(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);
+ }
#endregion
@@ -207,6 +270,7 @@ private string ExtractMemberName(Expression expression)
/// </summary>
/// <param name="expression">対象となる式</param>
/// <returns>値</returns>
+ /// <remarks>右辺専用</remarks>
private object ExtractValue(Expression expression)
{
//--- 定数
@@ -309,6 +373,19 @@ private object ExtractValue(Expression expression)
}
return value;
}
+
+
+ /// <summary>
+ /// 指定された式がbool型のプロパティかどうかを判定します。
+ /// </summary>
+ /// <param name="expression">対象となる式</param>
+ /// <returns>bool型のプロパティの場合true</returns>
+ /// <remarks>左辺専用</remarks>
+ private bool IsBooleanProperty(MemberExpression expression)
+ => expression != null
+ && expression.Member.MemberType == MemberTypes.Property
+ && expression.Expression == this.Parameter
+ && ((PropertyInfo)expression.Member).PropertyType == typeof(bool);
#endregion
View
@@ -24,6 +24,9 @@ public class Person
public int Age { get; set; }
+ public bool HasChildren { get; set; }
+
+
[NotMapped]
public int Sex { get; set; }
}
@@ -368,6 +368,51 @@ public void Contains()
actual.Parameter.Is(expectParameter);
actual.Statement.Is(expectStatement);
}
+
+
+ [TestMethod]
+ public void Boolean()
+ {
+ var actual = PredicateSql.From<Person>(DbKind.SqlServer, x => x.HasChildren);
+
+ var expectStatement = "HasChildren = @p0";
+ IDictionary<string, object> expectParameter = new ExpandoObject();
+ expectParameter.Add("p0", true);
+
+ actual.Parameter.Is(expectParameter);
+ actual.Statement.Is(expectStatement);
+ }
+
+
+ [TestMethod]
+ public void InverseBoolean()
+ {
+ var actual = PredicateSql.From<Person>(DbKind.SqlServer, x => !x.HasChildren);
+
+ var expectStatement = "HasChildren <> @p0";
+ IDictionary<string, object> expectParameter = new ExpandoObject();
+ expectParameter.Add("p0", true);
+
+ actual.Parameter.Is(expectParameter);
+ actual.Statement.Is(expectStatement);
+ }
+
+
+ [TestMethod]
+ public void BooleanAndOr()
+ {
+ var actual = PredicateSql.From<Person>(DbKind.SqlServer, x => x.HasChildren == true || x.Id != 0 || x.Name == "xin9le" && !x.HasChildren);
+
+ var expectStatement = "HasChildren = @p0 or Id <> @p1 or (名前 = @p2 and HasChildren <> @p3)";
+ IDictionary<string, object> expectParameter = new ExpandoObject();
+ expectParameter.Add("p0", true);
+ expectParameter.Add("p1", 0);
+ expectParameter.Add("p2", "xin9le");
+ expectParameter.Add("p3", true);
+
+ actual.Parameter.Is(expectParameter);
+ actual.Statement.Is(expectStatement);
+ }
}
@@ -31,7 +31,8 @@ public class PrimitiveSqlTest
@"select
Id as Id,
名前 as Name,
- Age as Age
+ Age as Age,
+ HasChildren as HasChildren
from dbo.Person";
actual1.Is(expect);
actual2.Is(expect);
@@ -82,12 +83,14 @@ public class PrimitiveSqlTest
@"insert into dbo.Person
(
名前,
- Age
+ Age,
+ HasChildren
)
values
(
@Name,
- next value for dbo.AgeSeq
+ next value for dbo.AgeSeq,
+ @HasChildren
)";
actual1.Is(expect);
actual2.Is(expect);
@@ -103,12 +106,14 @@ public class PrimitiveSqlTest
@"insert into dbo.Person
(
名前,
- Age
+ Age,
+ HasChildren
)
values
(
@Name,
- @Age
+ @Age,
+ @HasChildren
)";
actual1.Is(expect);
actual2.Is(expect);
@@ -125,13 +130,15 @@ public class PrimitiveSqlTest
(
Id,
名前,
- Age
+ Age,
+ HasChildren
)
values
(
@Id,
@Name,
- next value for dbo.AgeSeq
+ next value for dbo.AgeSeq,
+ @HasChildren
)";
actual1.Is(expect);
actual2.Is(expect);
@@ -149,7 +156,8 @@ public class PrimitiveSqlTest
@"update dbo.Person
set
名前 = @Name,
- Age = @Age";
+ Age = @Age,
+ HasChildren = @HasChildren";
actual1.Is(expect);
actual2.Is(expect);
}
@@ -198,7 +206,8 @@ public class PrimitiveSqlTest
set
Id = @Id,
名前 = @Name,
- Age = @Age";
+ Age = @Age,
+ HasChildren = @HasChildren";
actual1.Is(expect);
actual2.Is(expect);
}

0 comments on commit 8344231

Please sign in to comment.