/// <summary>
 /// Initialize filters
 /// </summary>
 private static void InitializeFilters()
 {
     // Try to init extended filters
     foreach (var ext in AppDomain.CurrentDomain.GetAssemblies()
              .Where(a => !a.IsDynamic)
              .SelectMany(a => { try { return(a.ExportedTypes); } catch { return(Type.EmptyTypes); } })
              .Where(t => typeof(IQueryFilterExtension).IsAssignableFrom(t) && !t.IsAbstract)
              .Select(t => Activator.CreateInstance(t) as IQueryFilterExtension))
     {
         QueryFilterExtensions.AddExtendedFilter(ext);
     }
 }
Exemplo n.º 2
0
            /// <summary>
            /// Extract the path
            /// </summary>
            /// <returns>The path.</returns>
            /// <param name="access">Access.</param>
            /// <param name="fromUnary">Extract the path from a unuary or binary expression</param>
            /// <param name="fromOperand">Indicates the extraction should occur from an operand and not the operator</param>
            protected String ExtractPath(Expression access, bool fromUnary, bool fromOperand = false)
            {
                access = this.StripConvert(access);
                if (access.NodeType == ExpressionType.MemberAccess)
                {
                    MemberExpression memberExpr = access as MemberExpression;
                    String           path       = this.ExtractPath(memberExpr.Expression, fromUnary, fromOperand); // get the chain if required
                    if (memberExpr.Expression.Type.IsGenericType && memberExpr.Expression.Type.GetGenericTypeDefinition() == typeof(Nullable <>))
                    {
                        return(path);
                    }

                    // XML property?
                    var memberInfo = memberExpr.Expression.Type.GetRuntimeProperty(memberExpr.Member.Name + "Xml") ??
                                     memberExpr.Member;

                    // Member information is declread on interface
                    Type mapType = null;
                    if (memberInfo.DeclaringType.IsInterface && this.m_interfaceHints.TryGetValue(memberInfo.DeclaringType, out mapType))
                    {
                        memberInfo = mapType.GetRuntimeProperty(memberInfo.Name) ?? memberInfo;
                    }

                    // Is this a delay load?
                    var serializationReferenceAttribute = memberExpr.Member.GetCustomAttribute <SerializationReferenceAttribute>();
                    var queryParameterAttribute         = memberExpr.Member.GetCustomAttribute <QueryParameterAttribute>();
                    var xmlIgnoreAttribute = memberExpr.Member.GetCustomAttribute <XmlIgnoreAttribute>();
                    if (xmlIgnoreAttribute != null && serializationReferenceAttribute != null && !String.IsNullOrEmpty(serializationReferenceAttribute.RedirectProperty))
                    {
                        memberInfo = memberExpr.Expression.Type.GetRuntimeProperty(serializationReferenceAttribute.RedirectProperty);
                    }

                    // TODO: Delay and bound properties!!
                    var memberXattribute = memberInfo.GetCustomAttributes <XmlElementAttribute>().FirstOrDefault();
                    if (memberXattribute == null && queryParameterAttribute != null)
                    {
                        memberXattribute = new XmlElementAttribute(queryParameterAttribute.ParameterName); // We don't serialize but it does exist
                    }
                    else if (memberExpr.Expression is ConstantExpression)
                    {
                        return((memberExpr.Expression as ConstantExpression).Value.ToString());
                    }
                    else if (memberXattribute == null)
                    {
                        if (memberExpr.Expression.Type.StripNullable() == typeof(DateTimeOffset) &&
                            memberExpr.Member.Name == "DateTime")
                        {
                            return(path);
                        }
                        throw new InvalidOperationException($"The path {access} cannot be translated, ensure the property is XML navigable or has a QueryParameter attribute"); // TODO: When this occurs?
                    }

                    // Return path
                    if (String.IsNullOrEmpty(path))
                    {
                        return(memberXattribute.ElementName);
                    }
                    else if (memberXattribute.ElementName == "id") // ID can be ignored
                    {
                        return(path);
                    }
                    else
                    {
                        return(String.Format("{0}.{1}", path, memberXattribute.ElementName));
                    }
                }
                else if (access.NodeType == ExpressionType.Call)
                {
                    //CallExpression callExpr = access as MemberExpression;
                    MethodCallExpression callExpr = access as MethodCallExpression;

                    if (callExpr.Method.Name == "Where" ||
                        fromUnary && (callExpr.Method.Name == "Any"))
                    {
                        String path            = this.ExtractPath(callExpr.Arguments[0], false, fromOperand); // get the chain if required
                        var    guardExpression = callExpr.Arguments[1] as LambdaExpression;
                        // Where should be a guard so we just grab the unary equals only!
                        var binaryExpression = guardExpression.Body as BinaryExpression;
                        if (binaryExpression == null)
                        {
                            throw new InvalidOperationException("Cannot translate non-binary expression guards");
                        }

                        // Is the expression the guard?
                        String guardString = this.BuildGuardExpression(binaryExpression);
                        return(String.Format("{0}[{1}]", path, guardString));
                    }
                    else if (callExpr.Method.Name == "First" ||
                             callExpr.Method.Name == "FirstOrDefault")
                    {
                        String path = this.ExtractPath(callExpr.Arguments[0], false, fromOperand); // get the chain if required
                        return(path);
                    }
                    else
                    {
                        var extendedFilter = QueryFilterExtensions.GetExtendedFilterByMethod(callExpr.Method);
                        if (extendedFilter != null)
                        {
                            return(this.ExtractPath(callExpr.Arguments[0], false, fromOperand)); // get the chain if required
                        }
                        else if (!s_reservedNames.Contains(callExpr.Method.Name))
                        {
                            throw new InvalidOperationException($"Can't find extended method handler for {callExpr.Method.Name}");
                        }
                    }
                }
                else if (access.NodeType == ExpressionType.TypeAs)
                {
                    UnaryExpression ua = (UnaryExpression)access;
                    return(String.Format("{0}@{1}", this.ExtractPath(ua.Operand, false, fromOperand), ua.Type.GetCustomAttribute <XmlTypeAttribute>().TypeName));
                }
                else if (access.NodeType == ExpressionType.Parameter && fromOperand)
                {
                    return("$_");
                }
                else if (access.NodeType == ExpressionType.Coalesce)
                {
                    BinaryExpression ba = (BinaryExpression)access;
                    return(this.ExtractPath(ba.Left, fromUnary, fromOperand) ?? this.ExtractPath(ba.Right, fromUnary, fromOperand));
                }
                return(null);
            }
Exemplo n.º 3
0
            /// <summary>
            /// Visit a binary expression which is in the form of A(operator)B
            /// </summary>
            /// <returns>The binary.</returns>
            /// <param name="node">Node.</param>
            protected override Expression VisitBinary(BinaryExpression node)
            {
                var left  = this.Visit(node.Left);
                var right = this.Visit(node.Right);

                String parmName  = this.ExtractPath(node.Left, false);
                Object parmValue = this.ExtractValue(node.Right);

                // Not able to map
                if ((node.Left as MethodCallExpression)?.Method.Name == "Any" &&
                    (node.Left as MethodCallExpression)?.Arguments.Count == 1)
                {
                    // Special exists method call - i.e. HAS X
                    var mci = (node.Left as MethodCallExpression);
                    parmName = this.ExtractPath(mci.Arguments[0], false);
                    if (node.Right is ConstantExpression cci)
                    {
                        if (node.NodeType == ExpressionType.NotEqual)
                        {
                            this.AddCondition(parmName, true.Equals(cci.Value) ? "null" : "!null");
                        }
                        else if (node.NodeType == ExpressionType.Equal)
                        {
                            this.AddCondition(parmName, true.Equals(cci.Value) ? "!null" : "null");
                        }
                        else
                        {
                            throw new InvalidOperationException($"Cannot determine how to convert ANY() function '{node}'");
                        }
                    }
                    else
                    {
                        this.AddCondition(parmName, "null");
                    }
                }
                else if (!String.IsNullOrEmpty(parmName))
                {
                    Object fParmValue = this.PrepareValue(parmValue, false);
                    if (parmValue is DateTime)
                    {
                        fParmValue = ((DateTime)parmValue).ToString("o");
                    }
                    else if (parmValue is DateTimeOffset)
                    {
                        fParmValue = ((DateTimeOffset)parmValue).ToString("o");
                    }
                    else if (parmValue == null)
                    {
                        fParmValue = "null";
                    }

                    // Node type
                    switch (node.NodeType)
                    {
                    case ExpressionType.GreaterThan:
                        fParmValue = ">" + fParmValue;
                        break;

                    case ExpressionType.GreaterThanOrEqual:
                        fParmValue = ">=" + fParmValue;
                        break;

                    case ExpressionType.LessThan:
                        fParmValue = "<" + fParmValue;
                        break;

                    case ExpressionType.LessThanOrEqual:
                        fParmValue = "<=" + fParmValue;
                        break;

                    case ExpressionType.NotEqual:
                        fParmValue = "!" + fParmValue;
                        break;
                    }

                    // Is this an extended method?
                    if (node.Left is MethodCallExpression mce)
                    {
                        if (left != null)
                        {
                            return(node);
                        }
                        else // we have to add it as it hasn't alread been added
                        {
                            var extendedFn = QueryFilterExtensions.GetExtendedFilterByMethod(mce.Method);
                            if (extendedFn != null)
                            {
                                var callValue = $":({extendedFn.Name}";
                                if (mce.Arguments.Count > 1)
                                {
                                    callValue += $"|{String.Join(",", mce.Arguments.Skip(1).Select(o => this.PrepareValue(this.ExtractValue(o), true)))}";
                                }
                                callValue += ")";
                                fParmValue = callValue + fParmValue;
                            }
                        }
                    }

                    this.AddCondition(parmName, fParmValue);
                }

                return(node);
            }
Exemplo n.º 4
0
            /// <summary>
            /// Visit method call optionally negating any values parsed
            /// </summary>
            private Expression VisitMethodCall(MethodCallExpression node, bool negate)
            {
                switch (node.Method.Name)
                {
                case "Contains":
                {
                    if (node.Object == null && node.Method.DeclaringType == typeof(Enumerable))
                    {
                        return(this.ParseArrayContains(node, negate));
                    }
                    else
                    {
                        var    parmName  = this.ExtractPath(node.Object, false);
                        object parmValue = this.ExtractValue(node.Arguments[0]);
                        this.AddCondition(parmName, (negate ? "!" : "~") + parmValue.ToString());
                        return(null);
                    }
                }

                case "StartsWith":
                {
                    var    parmName  = this.ExtractPath(node.Object, false);
                    object parmValue = this.ExtractValue(node.Arguments[0]);
                    this.AddCondition(parmName, (negate ? "!" : "^") + parmValue.ToString());
                    return(null);
                }

                case "Any":
                {
                    var parmName = this.ExtractPath(node.Arguments[0], false);
                    // Process lambda
                    var result = new List <KeyValuePair <string, object> >();
                    var subQueryExpressionVisitor = new HttpQueryExpressionVisitor(result, node.Arguments[0].Type);
                    if (node.Arguments.Count == 2)
                    {
                        subQueryExpressionVisitor.Visit(node.Arguments[1]);

                        // Result
                        foreach (var itm in result)
                        {
                            this.AddCondition(String.Format("{0}.{1}", parmName, itm.Key), negate ? $"!{itm.Value}" : itm.Value);
                        }
                        return(null);
                    }
                    else
                    {
                        return(null);
                    }
                }

                default:     // extended fn?
                {
                    var extendedFn = QueryFilterExtensions.GetExtendedFilterByMethod(node.Method);
                    if (extendedFn == null)
                    {
                        throw new MissingMemberException($"Cannot find extension method {node.Method}");
                    }

                    if (extendedFn.ExtensionMethod.ReturnType == typeof(bool))
                    {
                        var parmName = this.ExtractPath(node.Arguments[0], false);
                        if (parmName == null && typeof(IdentifiedData).IsAssignableFrom(node.Arguments[0].Type))
                        {
                            parmName = "id";
                        }
                        else if (parmName == null)
                        {
                            throw new InvalidOperationException($"Cannot determine how to map {extendedFn.Name}");
                        }
                        var callValue = $"{(negate ? "!" : "")}:({extendedFn.Name}";
                        if (node.Arguments.Count > 1)
                        {
                            callValue += $"|{String.Join(",", node.Arguments.Skip(1).Select(o => this.PrepareValue(this.ExtractValue(o), true)))}";
                        }
                        callValue += ")";

                        this.AddCondition(parmName, callValue);
                        return(node);
                    }
                    return(null);
                }
                }
            }