private static SqlExpression Recurse(ref int i, bool useCompression, bool useSingleTable, Expression expression, bool isUnary = false, string prefix = null, string postfix = null) { if (expression is UnaryExpression unary) { return(SqlExpression.Concat(unary.NodeType.ToSqlString(), Recurse(ref i, useCompression, useSingleTable, unary.Operand, true))); } if (expression is BinaryExpression body) { return(SqlExpression.Concat(Recurse(ref i, useCompression, useSingleTable, body.Left), body.NodeType.ToSqlString(), Recurse(ref i, useCompression, useSingleTable, body.Right))); } if (expression is ConstantExpression constant) { var value = constant.Value; if (value is int) { return(SqlExpression.TextOnly(value.ToString())); } if (value is string) { value = prefix + (string)value + postfix; } if (value is bool && isUnary) { return(SqlExpression.Concat(SqlExpression.WithSingleParameter(i++, value), "=", SqlExpression.TextOnly("1"))); } return(SqlExpression.WithSingleParameter(i++, value)); } if (expression is MemberExpression member) { var value = TryGetMemberValue(member); if (value != null) { return(GetMemberValue(ref i, value, prefix, postfix)); } if (member.Member is PropertyInfo property) { if (isUnary && member.Type == typeof(bool)) { return(SqlExpression.Concat(Recurse(ref i, useCompression, useSingleTable, expression), "=", SqlExpression.WithSingleParameter(i++, true))); } var name = expression.ToString(); if (name.Contains('.')) { name = string.Join(".", name.Split('.').Skip(1)); } name = TableHelper.GetFixedLeftPart(name, useCompression, useSingleTable); return(SqlExpression.TextOnly(name)); } if (member.Member is FieldInfo) { value = TryGetMemberValue(member); return(GetMemberValue(ref i, value, prefix, postfix)); } throw new Exception($"Expression does not refer to a property or field: {expression}"); } if (expression is MethodCallExpression methodCall) { // LIKE queries: if (methodCall.Method == typeof(string).GetMethod(nameof(string.Contains), new[] { typeof(string) })) { return(SqlExpression.Concat(Recurse(ref i, useCompression, useSingleTable, methodCall.Object), "LIKE", Recurse(ref i, useCompression, useSingleTable, methodCall.Arguments[0], prefix: "%", postfix: "%"))); } if (methodCall.Method == typeof(string).GetMethod(nameof(string.StartsWith), new[] { typeof(string) })) { return(SqlExpression.Concat(Recurse(ref i, useCompression, useSingleTable, methodCall.Object), "LIKE", Recurse(ref i, useCompression, useSingleTable, methodCall.Arguments[0], postfix: "%"))); } if (methodCall.Method == typeof(string).GetMethod(nameof(string.EndsWith), new[] { typeof(string) })) { return(SqlExpression.Concat(Recurse(ref i, useCompression, useSingleTable, methodCall.Object), "LIKE", Recurse(ref i, useCompression, useSingleTable, methodCall.Arguments[0], prefix: "%"))); } // IN queries: if (methodCall.Method.Name == nameof(IList.Contains)) { Expression collection; Expression property; if (methodCall.Method.IsDefined(typeof(ExtensionAttribute)) && methodCall.Arguments.Count == 2) { collection = methodCall.Arguments[0]; property = methodCall.Arguments[1]; } else if (!methodCall.Method.IsDefined(typeof(ExtensionAttribute)) && methodCall.Arguments.Count == 1) { collection = methodCall.Object; property = methodCall.Arguments[0]; } else { throw new Exception("Unsupported method call: " + methodCall.Method.Name); } var values = (IEnumerable)GetValue(collection); return(SqlExpression.Concat(Recurse(ref i, useCompression, useSingleTable, property), "IN", SqlExpression.WithParameters(ref i, values))); } throw new Exception("Unsupported method call: " + methodCall.Method.Name); } throw new Exception("Unsupported expression: " + expression.GetType().Name); }