static Expression <Func <TSource, bool> > BuildAllPredicate <TSource>( Type relatedType, BaseFilterQuery filter ) { var x_parameter = Expression.Parameter(relatedType, "x"); var propertyValues = filter.PropertyValue.Split(QueryConstants.COMMA); var member = Expression.Property(x_parameter, filter.Attribute.InternalAttributeName); var method = ContainsMethod.MakeGenericMethod(member.Type); var obj = TypeHelper.ConvertListType(propertyValues, member.Type); var tmp_parameter = Expression.Parameter(obj.GetType(), "tmp"); var selectCall = Expression.Call( typeof(Enumerable), "Select", new Type[] { member.Type, typeof(string) }, Expression.Lambda( member, x_parameter ) ); if (filter.FilterOperation == FilterOperations.all) { var contains = Expression.Call(method, new Expression[] { Expression.Constant(obj), member }); return(Expression.Lambda <Func <TSource, bool> >(contains, x_parameter)); } return(null); }
static Expression <Func <TSource, bool> > BuildAllCall <TSource>( ParameterExpression x_parameter, MemberExpression ienumerableMember, Type relatedType, BaseFilterQuery filter ) { var z_parameter = Expression.Parameter(relatedType, "z"); var z_member = Expression.Property(z_parameter, filter.Attribute.InternalAttributeName); var propertyValues = filter.PropertyValue.Split(QueryConstants.COMMA); var obj = TypeHelper.ConvertListType(propertyValues, z_member.Type); var tmp_variable = Expression.Parameter(obj.GetType(), "tmp"); var y_parameter = Expression.Parameter(z_member.Type, "y"); var tmp = Expression.Constant(obj); var selectCall = Expression.Call( typeof(Enumerable), nameof(Enumerable.Select), new Type[] { relatedType, z_member.Type }, ienumerableMember, Expression.Lambda( z_member, z_parameter ) ); var containsCall = Expression.Call(ContainsMethod.MakeGenericMethod(z_member.Type), new Expression[] { selectCall, y_parameter }); LambdaExpression allPredicate; if (filter.FilterOperation == FilterOperations.exclude) { allPredicate = Expression.Lambda(Expression.Not(containsCall), y_parameter); } else { allPredicate = Expression.Lambda(containsCall, y_parameter); } var allExpression = Expression.Call( typeof(Enumerable), nameof(Enumerable.All), new Type[] { y_parameter.Type }, Expression.Constant(obj), allPredicate ); return(Expression.Lambda(allExpression, x_parameter) as Expression <Func <TSource, bool> >); }
public void StaticExtensionTest() { Func <string, int> length = Type <CharEnumerator> .Method <string> .RequireStatic <int>(nameof(GetLength)); var str = "123"; Equal(3, length(str)); ContainsMethod contains = Type <CharEnumerator> .Method.Require <ContainsMethod>(nameof(Contains), MethodLookup.Static); True(contains(ref str, '3')); }
private bool ValidateKey(object[] keys) { var key = keys[0]; if (key == null) { return(false); } if ((bool)ContainsMethod.InvokeWithParameter(Value, key)) { return(false); } return(true); }
static Expression <Func <TSource, bool> > BuildAnyPredicate <TSource>( Type relatedType, BaseFilterQuery filter ) { var parameter = Expression.Parameter(relatedType, "x"); if (filter.FilterOperation == FilterOperations.@in || filter.FilterOperation == FilterOperations.nin) { var propertyValues = filter.PropertyValue.Split(QueryConstants.COMMA); var member = Expression.Property(parameter, filter.Attribute.InternalAttributeName); var method = ContainsMethod.MakeGenericMethod(member.Type); var obj = TypeHelper.ConvertListType(propertyValues, member.Type); if (filter.FilterOperation == FilterOperations.@in) { // Where(i => arr.Contains(i.column)) var contains = Expression.Call(method, new Expression[] { Expression.Constant(obj), member }); return(Expression.Lambda <Func <TSource, bool> >(contains, parameter)); } if (filter.FilterOperation == FilterOperations.nin) { // Where(i => !arr.Contains(i.column)) var notContains = Expression.Not(Expression.Call(method, new Expression[] { Expression.Constant(obj), member })); return(Expression.Lambda <Func <TSource, bool> >(notContains, parameter)); } return(null); } else { var left = Expression.PropertyOrField(parameter, filter.Attribute.InternalAttributeName); ConstantExpression right; if (filter.FilterOperation == FilterOperations.isnotnull || filter.FilterOperation == FilterOperations.isnull) { right = Expression.Constant(null); } else { // convert the incoming value to the target value type // "1" -> 1 var convertedValue = TypeHelper.ConvertType(filter.PropertyValue, left.Type); // {1} right = Expression.Constant(convertedValue, left.Type); } var body = GetFilterExpressionLambda(left, right, filter.FilterOperation); return(Expression.Lambda <Func <TSource, bool> >(body, parameter)); } }
private Expression Filter( ParameterExpression arg, string path, GenericSearchFilter filter, TFormat value) { var props = path.Split('.'); var type = typeof(T); Expression expr = arg; foreach (var prop in props) { // use reflection (not ComponentModel) to mirror LINQ var pi = type.GetProperty(prop); if (pi == null) { var msg = string.Format(CultureInfo.InvariantCulture, "Unknown property: {0} on type {1}", prop, type.FullName); if (prop != path) { msg += " for path " + path; } throw new ArgumentException(msg); } expr = Expression.Property(expr, pi); } switch (filter) { case GenericSearchFilter.Equals: return(Expression.Equal(expr, Expression.Constant(Deserialize(expr.Type, value)))); case GenericSearchFilter.NotEquals: return(Expression.NotEqual(expr, Expression.Constant(Deserialize(expr.Type, value)))); case GenericSearchFilter.LessThen: return(Expression.LessThan(expr, Expression.Constant(Deserialize(expr.Type, value)))); case GenericSearchFilter.LessOrEqualThen: return(Expression.LessThanOrEqual(expr, Expression.Constant(Deserialize(expr.Type, value)))); case GenericSearchFilter.GreaterThen: return(Expression.GreaterThan(expr, Expression.Constant(Deserialize(expr.Type, value)))); case GenericSearchFilter.GreaterThenOrEqual: return(Expression.GreaterThanOrEqual(expr, Expression.Constant(Deserialize(expr.Type, value)))); case GenericSearchFilter.InValue: return(Expression.Call( ContainsMethod.MakeGenericMethod(expr.Type), Expression.Constant(Deserialize(expr.Type.MakeArrayType(), value)), expr)); case GenericSearchFilter.NotInValue: return(Expression.Not( Expression.Call(ContainsMethod.MakeGenericMethod(expr.Type), Expression.Constant(Deserialize(expr.Type, value)), expr))); case GenericSearchFilter.ValueIn: return(Expression.Call( ContainsMethod.MakeGenericMethod(expr.Type), expr, Expression.Constant(Deserialize(expr.Type.GetElementType(), value)))); case GenericSearchFilter.ValueNotIn: return(Expression.Not( Expression.Call( ContainsMethod.MakeGenericMethod(expr.Type), expr, Expression.Constant(Deserialize(expr.Type.GetElementType(), value))))); case GenericSearchFilter.StartsWithValue: return(Expression.Call(expr, StringStartsWith, Expression.Constant(Deserialize(expr.Type, value)))); case GenericSearchFilter.StartsWithCaseInsensitiveValue: return(Expression.Call( expr, StringStartsWithCaseInsensitive, Expression.Constant(Deserialize(expr.Type, value)), Expression.Constant(StringComparison.InvariantCultureIgnoreCase))); case GenericSearchFilter.NotStartsWithValue: return(Expression.Not( Expression.Call( expr, StringStartsWith, Expression.Constant(Deserialize(expr.Type, value))))); case GenericSearchFilter.NotStartsWithCaseInsensitiveValue: return(Expression.Not( Expression.Call( expr, StringStartsWithCaseInsensitive, Expression.Constant(Deserialize(expr.Type, value)), Expression.Constant(StringComparison.InvariantCultureIgnoreCase)))); case GenericSearchFilter.ValueStartsWith: return(Expression.Call( Expression.Constant(Deserialize(expr.Type, value)), StringStartsWith, expr)); case GenericSearchFilter.ValueStartsWithCaseInsensitive: return(Expression.Call( Expression.Constant(Deserialize(expr.Type, value)), StringStartsWithCaseInsensitive, expr, Expression.Constant(StringComparison.InvariantCultureIgnoreCase))); case GenericSearchFilter.NotValueStartsWith: return(Expression.Not( Expression.Call( Expression.Constant(Deserialize(expr.Type, value)), StringStartsWith, expr))); case GenericSearchFilter.NotValueStartsWithCaseInsensitive: return(Expression.Not( Expression.Call( Expression.Constant(Deserialize(expr.Type, value)), StringStartsWithCaseInsensitive, expr, Expression.Constant(StringComparison.InvariantCultureIgnoreCase)))); default: throw new ArgumentException("Unknown filter"); } }
/// <summary> /// Normalizes .Equals into == and Contains() into the appropriate stub. /// </summary> protected override Expression VisitMethodCall(MethodCallExpression node) { // Convert .Equals() into == if (node.Method.Name == "Equals" && node.Method.ReturnType == typeof(bool) && node.Method.GetParameters().Length == 1) { var obj = new ObjectNormalizer().Visit(node.Object) as MethodCallExpression; if (new ObjectNormalizer().Visit(node.Arguments[0]) is MethodCallExpression parameter && (obj != null && ((IsParseObjectGet(obj) && obj.Object is ParameterExpression) || (IsParseObjectGet(parameter) && (parameter.Object is ParameterExpression))))) { return(Expression.Equal(node.Object, node.Arguments[0])); } } // Convert the .Contains() into a ContainsStub if (node.Method != StringContains && node.Method.Name == "Contains" && node.Method.ReturnType == typeof(bool) && node.Method.GetParameters().Length <= 2) { var collection = node.Method.GetParameters().Length == 1 ? node.Object : node.Arguments[0]; var parameterIndex = node.Method.GetParameters().Length - 1; if (new ObjectNormalizer().Visit(node.Arguments[parameterIndex]) is MethodCallExpression parameter && (IsParseObjectGet(parameter) && parameter.Object is ParameterExpression)) { var genericContains = ContainsMethod.MakeGenericMethod(parameter.Type); return(Expression.Call(genericContains, collection, parameter)); } var target = new ObjectNormalizer().Visit(collection) as MethodCallExpression; var element = node.Arguments[parameterIndex]; if (target != null && (IsParseObjectGet(target) && target.Object is ParameterExpression)) { var genericContains = ContainsMethod.MakeGenericMethod(element.Type); return(Expression.Call(genericContains, target, element)); } } // Convert obj["foo.bar"].ContainsKey("baz") into obj.ContainsKey("foo.bar.baz") if (node.Method.Name == "ContainsKey" && node.Method.ReturnType == typeof(bool) && node.Method.GetParameters().Length == 1) { var getter = new ObjectNormalizer().Visit(node.Object) as MethodCallExpression; Expression target = null; string path = null; if (getter != null && (IsParseObjectGet(getter) && getter.Object is ParameterExpression)) { target = getter.Object; path = GetValue(getter.Arguments[0]) + "." + GetValue(node.Arguments[0]); return(Expression.Call(ContainsKeyMethod, target, Expression.Constant(path))); } else if (node.Object is ParameterExpression) { target = node.Object; path = GetValue(node.Arguments[0]) as string; } if (target != null && path != null) { return(Expression.Call(ContainsKeyMethod, target, Expression.Constant(path))); } } return(base.VisitMethodCall(node)); }
/// <summary> /// If a ! operator is used, this removes the ! and instead calls the equivalent /// function (so e.g. == becomes !=, < becomes >=, Contains becomes NotContains) /// </summary> protected override Expression VisitUnary(UnaryExpression node) { // Normalizes inversion if (node.NodeType == ExpressionType.Not) { var visitedOperand = Visit(node.Operand); var binaryOperand = visitedOperand as BinaryExpression; if (binaryOperand != null) { switch (binaryOperand.NodeType) { case ExpressionType.GreaterThan: return(Expression.LessThanOrEqual(binaryOperand.Left, binaryOperand.Right)); case ExpressionType.GreaterThanOrEqual: return(Expression.LessThan(binaryOperand.Left, binaryOperand.Right)); case ExpressionType.LessThan: return(Expression.GreaterThanOrEqual(binaryOperand.Left, binaryOperand.Right)); case ExpressionType.LessThanOrEqual: return(Expression.GreaterThan(binaryOperand.Left, binaryOperand.Right)); case ExpressionType.Equal: return(Expression.NotEqual(binaryOperand.Left, binaryOperand.Right)); case ExpressionType.NotEqual: return(Expression.Equal(binaryOperand.Left, binaryOperand.Right)); } } var methodCallOperand = visitedOperand as MethodCallExpression; if (methodCallOperand != null) { if (methodCallOperand.Method.IsGenericMethod) { if (methodCallOperand.Method.GetGenericMethodDefinition() == ContainsMethod) { var genericNotContains = NotContainsMethod.MakeGenericMethod( methodCallOperand.Method.GetGenericArguments()); return(Expression.Call(genericNotContains, methodCallOperand.Arguments.ToArray())); } if (methodCallOperand.Method.GetGenericMethodDefinition() == NotContainsMethod) { var genericContains = ContainsMethod.MakeGenericMethod( methodCallOperand.Method.GetGenericArguments()); return(Expression.Call(genericContains, methodCallOperand.Arguments.ToArray())); } } if (methodCallOperand.Method == ContainsKeyMethod) { return(Expression.Call(NotContainsKeyMethod, methodCallOperand.Arguments.ToArray())); } if (methodCallOperand.Method == NotContainsKeyMethod) { return(Expression.Call(ContainsKeyMethod, methodCallOperand.Arguments.ToArray())); } } } return(base.VisitUnary(node)); }
private static IQueryable <TSource> CallGenericWhereContainsMethod <TSource>(IQueryable <TSource> source, BaseFilterQuery filter) { var concreteType = typeof(TSource); var property = concreteType.GetProperty(filter.Attribute.InternalAttributeName); try { var propertyValues = filter.PropertyValue.Split(QueryConstants.COMMA); ParameterExpression entity = Expression.Parameter(concreteType, "entity"); MemberExpression member; if (filter.IsAttributeOfRelationship) { var relation = Expression.PropertyOrField(entity, filter.Relationship.InternalRelationshipName); // Intercept the call if the relationship is type of "HasMany" if (typeof(IEnumerable).IsAssignableFrom(relation.Type)) { // Create the lambda using "Any" extension method var callExpr = BuildAnyCall(relation, relation.Type.GenericTypeArguments[0], filter); var lambda = Expression.Lambda <Func <TSource, bool> >(callExpr, entity); return(source.Where(lambda)); } member = Expression.Property(relation, filter.Attribute.InternalAttributeName); } else { member = Expression.Property(entity, filter.Attribute.InternalAttributeName); } var method = ContainsMethod.MakeGenericMethod(member.Type); var obj = TypeHelper.ConvertListType(propertyValues, member.Type); if (filter.FilterOperation == FilterOperations.@in) { // Where(i => arr.Contains(i.column)) var contains = Expression.Call(method, new Expression[] { Expression.Constant(obj), member }); var lambda = Expression.Lambda <Func <TSource, bool> >(contains, entity); return(source.Where(lambda)); } if (filter.FilterOperation == FilterOperations.nin) { // Where(i => !arr.Contains(i.column)) var notContains = Expression.Not(Expression.Call(method, new Expression[] { Expression.Constant(obj), member })); var lambda = Expression.Lambda <Func <TSource, bool> >(notContains, entity); return(source.Where(lambda)); } //if (filter.FilterOperation == FilterOperations.all) //{ // // Where(i => !arr.Contains(i.column)) // var notContains = Expression.Not(Expression.Call(method, new Expression[] { Expression.(obj), member })); // var lambda = Expression.Lambda<Func<TSource, bool>>(notContains, entity); // return source.Where(lambda); //} return(null); } catch (FormatException) { throw new JsonApiException(400, $"Could not cast {filter.PropertyValue} to {property.PropertyType.Name}"); } }