public override LambdaExpression VisitValPathExp(ScimFilterParser.ValPathExpContext context) { // brackets MAY change the field type (TResource) thus, the expression within the brackets // should be visited in context of the new field's type var argument = Expression.Parameter(typeof(TResource)); var attrPathExpression = Visit(context.attrPath()); if (attrPathExpression.ReturnType != typeof(TResource)) { Type childFilterType = attrPathExpression.ReturnType; bool isEnumerable = childFilterType.IsNonStringEnumerable(); if (isEnumerable) { childFilterType = childFilterType.GetGenericArguments()[0]; // set childFilterType to enumerable type argument } var childVisitorType = typeof(ScimFilterVisitor <>).MakeGenericType(childFilterType); var childVisitor = (IScimFilterVisitor)childVisitorType.CreateInstance(ServerConfiguration); var childLambda = childVisitor.VisitExpression(context.valPathFilter()); // Visit the nested filter expression. var childLambdaArgument = Expression.TryCatch( Expression.Block(Expression.Invoke(attrPathExpression, argument)), Expression.Catch(typeof(Exception), Expression.Constant(attrPathExpression.ReturnType.GetDefaultValue(), attrPathExpression.ReturnType)) ); if (isEnumerable) { // if we have an enumerable, then we need to see if any of its elements satisfy the childLambda // to accomplish this, let's just make use of .NET's Any<TSource>(enumerable, predicate) var anyMethod = MethodCache["any"].MakeGenericMethod(childFilterType); var anyPredicate = Expression.TryCatch( Expression.Block( Expression.Call( anyMethod, new List <Expression> { childLambdaArgument, childLambda })), Expression.Catch(typeof(ArgumentNullException), Expression.Constant(false))); return(Expression.Lambda(anyPredicate, argument)); } return(Expression.Lambda( Expression.Invoke( childLambda, Expression.Invoke(attrPathExpression, argument)), argument)); } // TODO: (DG) This is probably incorrect if the property is nested and the same type as its parent. // We'll most likely still need a childLambda. return(Visit(context.valPathFilter())); }
public override LambdaExpression VisitValPathExp(ScimFilterParser.ValPathExpContext context) { // brackets MAY change the field type (TResource) thus, the expression within the brackets // should be visited in context of the new field's type var propertyNameToken = context.FIELD().GetText(); var property = PropertyCache .GetOrAdd( typeof(TResource), type => type.GetProperties(BindingFlags.Public | BindingFlags.Instance) .ToDictionary(pi => pi.Name, pi => pi, StringComparer.OrdinalIgnoreCase))[propertyNameToken]; if (property == null) { throw new Exception("ERROR"); // TODO: (DG) make proper error } if (property.PropertyType != typeof(TResource)) { bool isEnumerable = property.PropertyType.IsNonStringEnumerable(); Type childFilterType = property.PropertyType; if (isEnumerable) { childFilterType = childFilterType.GetGenericArguments()[0]; // set childFilterType to enumerable type argument } var argument = Expression.Parameter(typeof(TResource)); var childVisitorType = typeof(ScimFilterVisitor <>).MakeGenericType(childFilterType); var childVisitor = (IScimFilterVisitor)childVisitorType.CreateInstance(); var childLambda = childVisitor.VisitExpression(context.valPathFilter()); // Visit the nested filter expression. var childLambdaArgument = Expression.TryCatch( Expression.Block(Expression.Property(argument, property)), Expression.Catch(typeof(Exception), Expression.Constant(property.PropertyType.GetDefaultValue(), property.PropertyType)) ); if (isEnumerable) { // if we have an enumerable, then we need to see if any of its elements satisfy the childLambda // to accomplish this, let's just make use of .NET's Any<TSource>(enumerable, predicate) var anyMethod = MethodCache["any"].MakeGenericMethod(childFilterType); var anyPredicate = Expression.TryCatch( Expression.Block( Expression.Call( anyMethod, new List <Expression> { childLambdaArgument, childLambda })), Expression.Catch(typeof(ArgumentNullException), Expression.Constant(false))); return(Expression.Lambda(anyPredicate, argument)); } return(Expression.Lambda( Expression.Invoke( childLambda, new List <Expression> { Expression.TypeAs(childLambdaArgument, childFilterType) }), argument)); } // TODO: (DG) This is probably incorrect if the property is nested and the same type as its parent. // We'll most likely still need a childLambda. return(Visit(context.valPathFilter())); }