/// <summary>
 /// Compiles a BinaryExpression where one of the parameters is null.
 /// </summary>
 /// <param name="parameter">The non-null parameter</param>
 private static string CompileNullBinaryExpression(BinaryExpression expression, CompileResult parameter)
 {
     if (expression.NodeType == ExpressionType.Equal)
     {
         return("(" + parameter.CommandText + " is null)");
     }
     else if (expression.NodeType == ExpressionType.NotEqual)
     {
         return("(" + parameter.CommandText + " is not null)");
     }
     else
     {
         throw new NotSupportedException("Cannot compile Null-BinaryExpression with type " + expression.NodeType.ToString());
     }
 }
        public static CompileResult CompileExpr(Expression expr, Dictionary <string, object> namesAndValues, string prefix, string name, bool isLeft = false)
        {
            if (expr == null)
            {
                throw new NotSupportedException("Expression is NULL");
            }
            else if (expr is LambdaExpression)
            {
                return(CompileExpr((expr as LambdaExpression).Body, namesAndValues, prefix, name, isLeft));
            }
            else if (expr is BinaryExpression)
            {
                var bin = (BinaryExpression)expr;

                var leftr  = CompileExpr(bin.Left, namesAndValues, prefix, name, true);
                var rightr = CompileExpr(bin.Right, namesAndValues, prefix, name, false);

                //If either side is a parameter and is null, then handle the other side specially (for "is null"/"is not null")
                string text;
                if (leftr.CommandText == "?" && leftr.Value == null)
                {
                    text = CompileNullBinaryExpression(bin, rightr);
                }
                else if (rightr.CommandText == "?" && rightr.Value == null)
                {
                    text = CompileNullBinaryExpression(bin, leftr);
                }
                else if (leftr.CommandText != "?" && rightr.CommandText == "?")
                {
                    if (string.IsNullOrEmpty(leftr.ParameterName))
                    {
                        var _split     = ("." + leftr.CommandText).Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
                        var _parameter = GetNewParameter(_split.Aggregate("", (_current, _s) => _current + _s));

                        text = "(" + leftr.CommandText + " " + GetOperand(bin) + " " + prefix + _parameter + ")";

                        namesAndValues.Add(_parameter, rightr.Value);
                    }
                    else
                    {
                        text = "(" + leftr.CommandText + " " + GetOperand(bin) + " " + prefix + leftr.ParameterName + ")";
                    }
                }
                else
                {
                    text = "(" + leftr.CommandText + " " + GetOperand(bin) + " " + rightr.CommandText + ")";
                }

                return(new CompileResult {
                    CommandText = text
                });
            }
            else if (expr.NodeType == ExpressionType.Call)
            {
                var call = (MethodCallExpression)expr;
                var args = new CompileResult[call.Arguments.Count];
                var obj  = call.Object != null?CompileExpr(call.Object, namesAndValues, prefix, name, isLeft) : null;

                for (var i = 0; i < args.Length; i++)
                {
                    args[i] = CompileExpr(call.Arguments[i], namesAndValues, prefix, name, isLeft);
                }

                // CHANGE ID = 1 -> comentado ABAIXO pois no momento da definição precisava concatenar com os caracteres "%" no início e/ou no final
                //                  quando usado like com apenas um parametro
                //                  validar em outras situações

                var sqlCall  = "";
                var _parName = "";

                if (call.Method.Name == "Like" && args.Length == 2)
                {
                    sqlCall = "(" + args[0].CommandText + " like " + args[1].CommandText + ")";
                }
                else if (call.Method.Name == "Contains" && args.Length >= 2)
                {
                    sqlCall = $"({args[1].CommandText} in {args[0].CommandText})";
                }
                else if (call.Method.Name == "Contains" && args.Length == 1)
                {
                    if (call.Object != null && call.Object.Type == typeof(string))
                    {
                        // CHANGE ID = 1
                        var _parSQL    = $"{name}.{obj.CommandText}";
                        var _parameter = $"{name}{obj.CommandText}";

                        sqlCall = $"({_parSQL} like {prefix}{_parameter})";

                        namesAndValues.Add(_parameter, $"%{args[0].Value}%");
                    }
                    else
                    {
                        // CHANGE ID = 1
                        sqlCall = string.Format("({0}.{1} in ({2}{1}))", name, obj.CommandText, prefix);
                    }
                }
                else if (call.Method.Name == "StartsWith" && args.Length == 1)
                {
                    // CHANGE ID = 1
                    var _parSQL    = $"{name}.{obj.CommandText}";
                    var _parameter = $"{name}{obj.CommandText}";

                    sqlCall = $"({_parSQL} like {prefix}{_parameter})";

                    namesAndValues.Add(_parameter, $"{args[0].Value}%");
                }
                else if (call.Method.Name == "EndsWith" && args.Length == 1)
                {
                    // CHANGE ID = 1
                    var _parSQL    = $"{name}.{obj.CommandText}";
                    var _parameter = $"{name}{obj.CommandText}";

                    sqlCall = $"({_parSQL} like {prefix}{_parameter})";

                    namesAndValues.Add(_parameter, $"%{args[0].Value}");
                }
                else if (call.Method.Name == "Equals" && args.Length == 1)
                {
                    sqlCall = "(" + obj.CommandText + " = (" + args[0].CommandText + "))";
                }
                else if (call.Method.Name == "ToLower")
                {
                    sqlCall = "(lower(" + obj.CommandText + "))";
                }
                else if (call.Method.Name == "ToUpper")
                {
                    sqlCall = "(upper(" + obj.CommandText + "))";
                }
                else if (call.Method.Name == "Trim")
                {
                    sqlCall  = "(ltrim(rtrim(" + obj.CommandText + ")))";
                    _parName = obj.CommandText;
                }
                else
                {
                    sqlCall = call.Method.Name.ToLower() + "(" + string.Join(",", args.Select(a => a.CommandText).ToArray()) + ")";
                }
                return(new CompileResult {
                    CommandText = sqlCall, ParameterName = _parName
                });
            }
            else if (expr.NodeType == ExpressionType.Constant)
            {
                var _c = (ConstantExpression)expr;

                return(new CompileResult
                {
                    CommandText = "?",
                    Value = _c.Value
                });
            }
            else if (expr.NodeType == ExpressionType.Convert)
            {
                var u    = (UnaryExpression)expr;
                var ty   = u.Type;
                var valr = CompileExpr(u.Operand, namesAndValues, prefix, name, isLeft);
                return(new CompileResult
                {
                    CommandText = valr.CommandText,
                    Value = valr.Value != null?ConvertTo(valr.Value, ty) : null
                });
            }
            else if (expr.NodeType == ExpressionType.MemberAccess)
            {
                var           _mem     = (MemberExpression)expr;
                var           _joinAtt = _mem.Member.GetCustomAttributes(typeof(JoinFieldAttribute));
                CompileResult _rr      = null;

                if (_mem.Expression != null && _mem.Expression.NodeType == ExpressionType.MemberAccess && !_joinAtt.Any())
                {
                    _rr = CompileExpr(_mem.Expression, namesAndValues, prefix, name, isLeft);
                }

                if (_mem.Expression != null && _mem.Expression.NodeType == ExpressionType.Parameter)
                {
                    //
                    // This is a column of our table, output just the column name
                    // Need to translate it if that column name is mapped
                    //

                    if (_joinAtt.Any())
                    {
                        var _c = (isLeft ? _mem.Type.Name + "." : "");// + ((PropertyInfo)_mem.Member).Name;
                        return(new CompileResult {
                            CommandText = _c, Value = ""
                        });
                    }

                    //var _columnName = (isLeft ? string.Format("{0}.", name) : "") + _mem.Member.Name;
                    var _columnName = $"{name}.{_mem.Member.Name}";

                    return(new CompileResult {
                        CommandText = _columnName
                    });
                }
                else
                {
                    object obj = null;
                    if (_mem.Expression != null)
                    {
                        var r = CompileExpr(_mem.Expression, namesAndValues, prefix, name, isLeft);
                        if (r.Value == null)
                        {
                            throw new NotSupportedException("Member access failed to compile expression");
                        }
                        obj = r.Value;
                    }

                    //
                    // Get the member value
                    //
                    object val = null;

                    if (_mem.Member is PropertyInfo)
                    {
                        if (_rr != null)
                        {
                            var _pp = (_mem.Member as PropertyInfo);
                            if (_rr.CommandText.Equals("?"))
                            {
                                _rr.Value = _pp.GetValue(obj);
                            }
                        }
                        else
                        {
                            var m = (PropertyInfo)_mem.Member;
                            val = m.GetValue(obj, null);
                        }
                    }
                    else if (_mem.Member is FieldInfo)
                    {
                        var m = (FieldInfo)_mem.Member;
                        val = m.GetValue(obj);
                    }
                    else
                    {
                        throw new NotSupportedException("MemberExpr: " + _mem.Member.DeclaringType);
                    }

                    //
                    // Work special magic for enumerables
                    //
                    if (val != null && val is System.Collections.IEnumerable && !(val is string) && !(val is System.Collections.Generic.IEnumerable <byte>))
                    {
                        var sb = new System.Text.StringBuilder();
                        sb.Append("(");
                        var head   = "";
                        var _count = 0;
                        foreach (var a in (System.Collections.IEnumerable)val)
                        {
                            _count++;
                            var _parameter = string.Format("{0}{1}{2}", name, _mem.Member.Name, _count);
                            namesAndValues.Add(_parameter, a);

                            sb.Append(head);

                            sb.AppendFormat("{0}{1}{2}{3}", prefix, name, _mem.Member.Name, _count);
                            head = ",";
                        }
                        sb.Append(")");
                        return(new CompileResult
                        {
                            CommandText = sb.ToString(),
                            Value = val
                        });
                    }
                    else
                    {
                        if (_rr != null)
                        {
                            return(_rr);
                        }

                        return(new CompileResult
                        {
                            CommandText = "?",
                            Value = val
                        });
                    }
                }
            }
            throw new NotSupportedException("Cannot compile: " + expr.NodeType.ToString());
        }