Пример #1
0
        public virtual SqlExpression TranslateMemberAccess(
            [NotNull] SqlExpression instance, [NotNull] SqlExpression member, [NotNull] Type returnType)
        {
            // The first time we see a JSON traversal it's on a column - create a JsonTraversalExpression.
            // Traversals on top of that get appended into the same expression.

            if (instance is ColumnExpression columnExpression &&
                columnExpression.TypeMapping is MySqlJsonTypeMapping)
            {
                return(ConvertFromJsonExtract(
                           _sqlExpressionFactory.JsonTraversal(
                               columnExpression,
                               returnsText: returnType == typeof(string),
                               returnType)
                           .Append(
                               member,
                               returnType,
                               FindPocoTypeMapping(returnType)),
                           returnType));
            }

            if (instance is MySqlJsonTraversalExpression prevPathTraversal)
            {
                return(prevPathTraversal.Append(
                           member,
                           returnType,
                           FindPocoTypeMapping(returnType)));
            }

            return(null);
        }
        public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments)
        {
            if (method.DeclaringType != typeof(JsonElement) ||
                !(instance.TypeMapping is MySqlJsonTypeMapping mapping))
            {
                return(null);
            }

            // The root of the JSON expression is a ColumnExpression. We wrap that with an empty traversal
            // expression (col->'$'); subsequent traversals will gradually append the path into that.
            // Note that it's possible to call methods such as GetString() directly on the root, and the
            // empty traversal is necessary to properly convert it to a text.
            instance = instance is ColumnExpression columnExpression
                ? _sqlExpressionFactory.JsonTraversal(
                columnExpression, returnsText : false, typeof(string), mapping)
                : instance;

            if (method == _getProperty)
            {
                return(instance is MySqlJsonTraversalExpression prevPathTraversal
                    ? prevPathTraversal.Append(
                           ApplyPathLocationTypeMapping(arguments[0]),
                           typeof(JsonElement),
                           _typeMappingSource.FindMapping(typeof(JsonElement)))
                    : null);
            }

            if (method == _arrayIndexer)
            {
                return(instance is MySqlJsonTraversalExpression prevPathTraversal
                    ? prevPathTraversal.Append(
                           _sqlExpressionFactory.JsonArrayIndex(ApplyPathLocationTypeMapping(arguments[0])),
                           typeof(JsonElement),
                           _typeMappingSource.FindMapping(typeof(JsonElement)))
                    : null);
            }

            if (_getMethods.Contains(method.Name) &&
                arguments.Count == 0 &&
                instance is MySqlJsonTraversalExpression traversal)
            {
                return(ConvertFromJsonExtract(
                           traversal.Clone(
                               method.Name == nameof(JsonElement.GetString),
                               method.ReturnType,
                               _typeMappingSource.FindMapping(method.ReturnType)
                               ),
                           method.ReturnType));
            }

            if (method == _getArrayLength)
            {
                return(_sqlExpressionFactory.Function(
                           "JSON_LENGTH",
                           new[] { instance },
                           typeof(int)));
            }

            if (method.Name.StartsWith("TryGet") && arguments.Count == 0)
            {
                throw new InvalidOperationException($"The TryGet* methods on {nameof(JsonElement)} aren't translated yet, use Get* instead.'");
            }

            return(null);
        }
        public SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments, IDiagnosticsLogger <DbLoggerCategory.Query> logger)
        {
            if (instance != null && instance.Type.IsGenericList() && method.Name == "get_Item" && arguments.Count == 1)
            {
                // Try translating indexing inside json column
                return(_jsonPocoTranslator.TranslateMemberAccess(instance, arguments[0], method.ReturnType));
            }

            // Predicate-less Any - translate to a simple length check.
            if (method.IsClosedFormOf(_enumerableAnyWithoutPredicate) &&
                arguments.Count == 1 &&
                arguments[0].Type.TryGetElementType(out _) &&
                arguments[0].TypeMapping is MySqlJsonTypeMapping)
            {
                return(_sqlExpressionFactory.GreaterThan(
                           _jsonPocoTranslator.TranslateArrayLength(arguments[0]),
                           _sqlExpressionFactory.Constant(0)));
            }

            if (method.DeclaringType != typeof(JsonElement) ||
                !(instance.TypeMapping is MySqlJsonTypeMapping mapping))
            {
                return(null);
            }

            // The root of the JSON expression is a ColumnExpression. We wrap that with an empty traversal
            // expression (col->'$'); subsequent traversals will gradually append the path into that.
            // Note that it's possible to call methods such as GetString() directly on the root, and the
            // empty traversal is necessary to properly convert it to a text.
            instance = instance is ColumnExpression columnExpression
                ? _sqlExpressionFactory.JsonTraversal(
                columnExpression, returnsText : false, typeof(string), mapping)
                : instance;

            if (method == _getProperty)
            {
                return(instance is MySqlJsonTraversalExpression prevPathTraversal
                    ? prevPathTraversal.Append(
                           ApplyPathLocationTypeMapping(arguments[0]),
                           typeof(JsonElement),
                           _typeMappingSource.FindMapping(typeof(JsonElement)))
                    : null);
            }

            if (method == _arrayIndexer)
            {
                return(instance is MySqlJsonTraversalExpression prevPathTraversal
                    ? prevPathTraversal.Append(
                           _sqlExpressionFactory.JsonArrayIndex(ApplyPathLocationTypeMapping(arguments[0])),
                           typeof(JsonElement),
                           _typeMappingSource.FindMapping(typeof(JsonElement)))
                    : null);
            }

            if (_getMethods.Contains(method.Name) &&
                arguments.Count == 0 &&
                instance is MySqlJsonTraversalExpression traversal)
            {
                return(ConvertFromJsonExtract(
                           traversal.Clone(
                               method.Name == nameof(JsonElement.GetString),
                               method.ReturnType,
                               _typeMappingSource.FindMapping(method.ReturnType)
                               ),
                           method.ReturnType));
            }

            if (method == _getArrayLength)
            {
                // Could return NULL if the path is not found, but we would be alright to throw then.
                return(_sqlExpressionFactory.NullableFunction(
                           "JSON_LENGTH",
                           new[] { instance },
                           typeof(int),
                           false));
            }

            if (method.Name.StartsWith("TryGet") && arguments.Count == 0)
            {
                throw new InvalidOperationException($"The TryGet* methods on {nameof(JsonElement)} aren't translated yet, use Get* instead.'");
            }

            return(null);
        }