protected override Expression VisitBinary(BinaryExpression node) { using var mc = log?.Enter(LogLevel.LinqExpression, node); if (ExpressionTreeHelpers.SupportedComparisons.ContainsKey(node.NodeType)) { foreach (var prop in TypeProperties) { if (ExpressionTreeHelpers.IsMatchingMemberValueExpression(node, typeof(T), prop.Name)) { object testValue = TranslateValue(prop, ExpressionTreeHelpers.GetValueFromExpression(node, typeof(T), prop.Name)); AddClause($"[{TranslateFieldName(prop)}] {ExpressionTreeHelpers.SupportedComparisons[node.NodeType]} '{testValue}' "); return(node); } } if (node.Left.NodeType == ExpressionType.Call) { object testValue = TranslateValue(null, ExpressionTreeHelpers.GetValueFromExpression(node.Right)); futureComparisons.Push($"{ExpressionTreeHelpers.SupportedComparisons[node.NodeType]} '{testValue}'"); } return(base.VisitBinary(node)); } else if (node.NodeType == ExpressionType.OrElse || node.NodeType == ExpressionType.Or) { log?.WriteLine(LogLevel.LinqQuery, $"\t[{node.NodeType}]: {node.Left.NodeType} vs. {node.Right.NodeType}"); AddClause("("); base.Visit(node.Left); AddClause(" OR "); base.Visit(node.Right); AddClause(")"); return(node); } else if (node.NodeType == ExpressionType.AndAlso || node.NodeType == ExpressionType.And) { log?.WriteLine(LogLevel.LinqQuery, $"\t[{node.NodeType}]: {node.Left.NodeType} vs. {node.Right.NodeType}"); AddClause("("); base.Visit(node.Left); AddClause(" AND "); base.Visit(node.Right); AddClause(")"); return(node); } else { log?.WriteLine(LogLevel.LinqQuery, $"ERROR: unsupported comparison: {node.NodeType}"); return(base.VisitBinary(node)); } }
protected override Expression VisitMethodCall(MethodCallExpression node) { string prefix = (futureComparisons.TryPop(out string literalCompare) && literalCompare == "= 'False'") ? "NOT " : ""; using var mc = log?.Enter(LogLevel.LinqExpression, node); // member.Contains(...) || member.StartsWith(...) if (node.Method.DeclaringType == typeof(string) && (node.Method.Name == nameof(string.StartsWith) || node.Method.Name == nameof(string.Contains))) { foreach (var prop in TypeProperties.Where(prop => prop.PropertyType == typeof(string))) { if (ExpressionTreeHelpers.IsSpecificMemberExpression(node.Object, typeof(T), prop.Name)) { object testValue = TranslateValue(prop, ExpressionTreeHelpers.GetValueFromExpression(node.Arguments[0])); AddClause($"{prefix}[{TranslateFieldName(prop)}] CONTAINS '{testValue}' "); return(node); } } } // array.Contains(member) || List<string>.Contains(member) else if (node.Method.Name == nameof(string.Contains)) { PropertyInfo propertyInfo = null; Expression valuesExpression = null; if (node.Method.DeclaringType == typeof(Enumerable)) { foreach (var prop in TypeProperties) { if (ExpressionTreeHelpers.IsSpecificMemberExpression(node.Arguments[0], typeof(T), prop.Name)) { propertyInfo = prop; valuesExpression = node.Arguments[1]; break; } else if (ExpressionTreeHelpers.IsSpecificMemberExpression(node.Arguments[1], typeof(T), prop.Name)) { propertyInfo = prop; valuesExpression = node.Arguments[0]; break; } } } else if (node.Method.DeclaringType == typeof(List <string>)) { foreach (var prop in TypeProperties) { if (ExpressionTreeHelpers.IsSpecificMemberExpression(node.Arguments[0], typeof(T), prop.Name)) { propertyInfo = prop; valuesExpression = node.Object; break; } if (ExpressionTreeHelpers.IsSpecificMemberExpression(node.Object, typeof(T), prop.Name)) { propertyInfo = prop; valuesExpression = node.Arguments[0]; break; } } } else { throw new Exception($"{node.Method.DeclaringType} must be Enumerable or List<string> to use 'Contains'"); } if (valuesExpression == null || valuesExpression.NodeType != ExpressionType.Constant) { throw new Exception($"'Contains' expression must compare to a set of literals - {valuesExpression?.NodeType}"); } ConstantExpression ce = (ConstantExpression)valuesExpression; if (ce.Value is string text) // single value { AddClause($"{prefix}[{TranslateFieldName(propertyInfo)}] CONTAINS '{ce.Value}' "); } else { // Add each string in the collection to the list of locations to obtain data about. var textValues = (IEnumerable <string>)ce.Value; AddClause($"{prefix}[{TranslateFieldName(propertyInfo)}] IN ({string.Join(',', textValues.Select(s => "'" + s + "'"))}) "); } return(node); } return(base.VisitMethodCall(node)); }