diff --git a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlJsonDomTranslator.cs b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlJsonDomTranslator.cs index 7a94102cb..ec838d19d 100644 --- a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlJsonDomTranslator.cs +++ b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlJsonDomTranslator.cs @@ -130,9 +130,11 @@ public NpgsqlJsonDomTranslator( typeof(string), _stringTypeMapping); + // The PostgreSQL traversal operator always returns text - for these scalar-returning methods, apply a conversion from string. return method.Name == nameof(JsonElement.GetString) ? traversalToText - : ConvertFromText(traversalToText, method.ReturnType); + : _sqlExpressionFactory.Convert( + traversalToText, method.ReturnType, _typeMappingSource.FindMapping(method.ReturnType, _model)); } if (method == GetArrayLength) @@ -152,30 +154,4 @@ public NpgsqlJsonDomTranslator( return null; } - - // The PostgreSQL traversal operator always returns text, so we need to convert to int, bool, etc. - private SqlExpression ConvertFromText(SqlExpression expression, Type returnType) - { - switch (Type.GetTypeCode(returnType)) - { - case TypeCode.Boolean: - case TypeCode.Byte: - case TypeCode.DateTime: - case TypeCode.Decimal: - case TypeCode.Double: - case TypeCode.Int16: - case TypeCode.Int32: - case TypeCode.Int64: - case TypeCode.SByte: - case TypeCode.Single: - case TypeCode.UInt16: - case TypeCode.UInt32: - case TypeCode.UInt64: - return _sqlExpressionFactory.Convert(expression, returnType, _typeMappingSource.FindMapping(returnType, _model)); - default: - return returnType == typeof(Guid) - ? _sqlExpressionFactory.Convert(expression, returnType, _typeMappingSource.FindMapping(returnType, _model)) - : expression; - } - } } diff --git a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlJsonPocoTranslator.cs b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlJsonPocoTranslator.cs index 61a0946c3..fbaf19142 100644 --- a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlJsonPocoTranslator.cs +++ b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlJsonPocoTranslator.cs @@ -97,7 +97,8 @@ public NpgsqlJsonPocoTranslator( /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual SqlExpression? TranslateMemberAccess(SqlExpression instance, SqlExpression member, Type returnType) - => instance switch + { + return instance switch { // 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. @@ -119,6 +120,25 @@ PgJsonTraversalExpression prevPathTraversal _ => null }; + // The PostgreSQL traversal operator always returns text. + // If the type returned is a scalar (int, bool, etc.), we need to apply a conversion from string. + SqlExpression ConvertFromText(SqlExpression expression, Type returnType) + => _typeMappingSource.FindMapping(returnType.UnwrapNullableType(), _model) switch + { + // Type mapping not found - this isn't a scalar + null => expression, + + // Arrays are dealt with as JSON arrays, not array scalars + NpgsqlArrayTypeMapping => expression, + + // Text types don't a a conversion to string + { StoreTypeNameBase: "text" or "varchar" or "char" } => expression, + + // For any other type mapping, this is a scalar; apply a conversion to the type from string. + var mapping => _sqlExpressionFactory.Convert(expression, returnType, mapping) + }; + } + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -159,38 +179,4 @@ PgJsonTraversalExpression prevPathTraversal return null; } } - - // The PostgreSQL traversal operator always returns text, so we need to convert to int, bool, etc. - private SqlExpression ConvertFromText(SqlExpression expression, Type returnType) - { - var unwrappedReturnType = returnType.UnwrapNullableType(); - - switch (Type.GetTypeCode(unwrappedReturnType)) - { - case TypeCode.Boolean: - case TypeCode.Byte: - case TypeCode.DateTime: - case TypeCode.Decimal: - case TypeCode.Double: - case TypeCode.Int16: - case TypeCode.Int32: - case TypeCode.Int64: - case TypeCode.SByte: - case TypeCode.Single: - case TypeCode.UInt16: - case TypeCode.UInt32: - case TypeCode.UInt64: - return _sqlExpressionFactory.Convert(expression, returnType, _typeMappingSource.FindMapping(returnType, _model)); - } - - if (unwrappedReturnType == typeof(Guid) - || unwrappedReturnType == typeof(DateTimeOffset) - || unwrappedReturnType == typeof(DateOnly) - || unwrappedReturnType == typeof(TimeOnly)) - { - return _sqlExpressionFactory.Convert(expression, returnType, _typeMappingSource.FindMapping(returnType, _model)); - } - - return expression; - } }