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); }