diff --git a/src/EFCore/Storage/CoreTypeMapping.cs b/src/EFCore/Storage/CoreTypeMapping.cs
index dba6280b10b..b301fe53e1e 100644
--- a/src/EFCore/Storage/CoreTypeMapping.cs
+++ b/src/EFCore/Storage/CoreTypeMapping.cs
@@ -4,6 +4,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using Microsoft.EntityFrameworkCore.Internal;
+using Microsoft.EntityFrameworkCore.Storage.Internal;
using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Storage;
@@ -23,6 +24,9 @@ namespace Microsoft.EntityFrameworkCore.Storage;
///
public abstract class CoreTypeMapping
{
+ private static readonly bool UseOldBehavior32376 =
+ AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue32376", out var enabled32376) && enabled32376;
+
///
/// Parameter object for use in the hierarchy.
///
@@ -130,15 +134,31 @@ public CoreTypeMappingParameters WithComposedConverter(
ProviderValueComparer,
ValueGeneratorFactory,
elementMapping ?? ElementTypeMapping,
- jsonValueReaderWriter
- ?? (converter == null || JsonValueReaderWriter == null
- ? JsonValueReaderWriter
- : RuntimeFeature.IsDynamicCodeSupported
- ? (JsonValueReaderWriter)Activator.CreateInstance(
- typeof(JsonConvertedValueReaderWriter<,>).MakeGenericType(
- converter.ModelClrType, JsonValueReaderWriter.ValueType),
- JsonValueReaderWriter, converter)!
- : throw new InvalidOperationException(CoreStrings.NativeAotNoCompiledModel)));
+ jsonValueReaderWriter ?? CreateReaderWriter(converter, JsonValueReaderWriter));
+
+ static JsonValueReaderWriter? CreateReaderWriter(ValueConverter? converter, JsonValueReaderWriter? readerWriter)
+ {
+ if (converter == null || readerWriter == null)
+ {
+ return readerWriter;
+ }
+
+ if (!RuntimeFeature.IsDynamicCodeSupported)
+ {
+ throw new InvalidOperationException(CoreStrings.NativeAotNoCompiledModel);
+ }
+
+ if (!UseOldBehavior32376
+ && readerWriter is IJsonConvertedValueReaderWriter convertedValueReaderWriter)
+ {
+ readerWriter = convertedValueReaderWriter.InnerReaderWriter;
+ }
+
+ return (JsonValueReaderWriter)Activator.CreateInstance(
+ typeof(JsonConvertedValueReaderWriter<,>).MakeGenericType(
+ converter.ModelClrType, readerWriter.ValueType),
+ readerWriter, converter)!;
+ }
}
}
diff --git a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs
index 03927467588..c2ca3379e7f 100644
--- a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs
+++ b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs
@@ -14593,12 +14593,8 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas
converter: new ValueConverter(
(string v) => string.Format(CultureInfo.InvariantCulture, "{0}", (object)(v.Length < 1 ? '\0' : v[0])),
(string v) => string.Format(CultureInfo.InvariantCulture, "{0}", (object)(v.Length < 1 ? '\0' : v[0]))),
- jsonValueReaderWriter: new JsonConvertedValueReaderWriter(
- new JsonConvertedValueReaderWriter(
- JsonStringReaderWriter.Instance,
- new ValueConverter(
- (char v) => string.Format(CultureInfo.InvariantCulture, "{0}", (object)v),
- (string v) => v.Length < 1 ? '\0' : v[0])),
+ jsonValueReaderWriter: new JsonConvertedValueReaderWriter(
+ JsonStringReaderWriter.Instance,
new ValueConverter(
(string v) => string.Format(CultureInfo.InvariantCulture, "{0}", (object)(v.Length < 1 ? '\0' : v[0])),
(string v) => string.Format(CultureInfo.InvariantCulture, "{0}", (object)(v.Length < 1 ? '\0' : v[0])))));
@@ -14787,12 +14783,8 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas
converter: new ValueConverter(
(string v) => (long)StringEnumConverter.ConvertToEnum(v),
(long value) => ((CSharpRuntimeModelCodeGeneratorTest.EnumU32)value).ToString()),
- jsonValueReaderWriter: new JsonConvertedValueReaderWriter(
- new JsonConvertedValueReaderWriter(
- JsonInt64ReaderWriter.Instance,
- new ValueConverter(
- (CSharpRuntimeModelCodeGeneratorTest.EnumU32 value) => (long)value,
- (long value) => (CSharpRuntimeModelCodeGeneratorTest.EnumU32)value)),
+ jsonValueReaderWriter: new JsonConvertedValueReaderWriter(
+ JsonInt64ReaderWriter.Instance,
new ValueConverter(
(string v) => (long)StringEnumConverter.ConvertToEnum(v),
(long value) => ((CSharpRuntimeModelCodeGeneratorTest.EnumU32)value).ToString())));
@@ -14946,12 +14938,8 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas
(string v) => new Uri(v, UriKind.RelativeOrAbsolute).ToString(),
(string v) => new Uri(v, UriKind.RelativeOrAbsolute).ToString()),
storeTypePostfix: StoreTypePostfix.None,
- jsonValueReaderWriter: new JsonConvertedValueReaderWriter(
- new JsonConvertedValueReaderWriter(
- JsonStringReaderWriter.Instance,
- new ValueConverter(
- (Uri v) => v.ToString(),
- (string v) => new Uri(v, UriKind.RelativeOrAbsolute))),
+ jsonValueReaderWriter: new JsonConvertedValueReaderWriter(
+ JsonStringReaderWriter.Instance,
new ValueConverter(
(string v) => new Uri(v, UriKind.RelativeOrAbsolute).ToString(),
(string v) => new Uri(v, UriKind.RelativeOrAbsolute).ToString())));
@@ -30407,12 +30395,8 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas
converter: new ValueConverter(
(string v) => string.Format(CultureInfo.InvariantCulture, "{0}", (object)(v.Length < 1 ? '\0' : v[0])),
(string v) => string.Format(CultureInfo.InvariantCulture, "{0}", (object)(v.Length < 1 ? '\0' : v[0]))),
- jsonValueReaderWriter: new JsonConvertedValueReaderWriter(
- new JsonConvertedValueReaderWriter(
- JsonStringReaderWriter.Instance,
- new ValueConverter(
- (char v) => string.Format(CultureInfo.InvariantCulture, "{0}", (object)v),
- (string v) => v.Length < 1 ? '\0' : v[0])),
+ jsonValueReaderWriter: new JsonConvertedValueReaderWriter(
+ JsonStringReaderWriter.Instance,
new ValueConverter(
(string v) => string.Format(CultureInfo.InvariantCulture, "{0}", (object)(v.Length < 1 ? '\0' : v[0])),
(string v) => string.Format(CultureInfo.InvariantCulture, "{0}", (object)(v.Length < 1 ? '\0' : v[0])))));
@@ -30601,12 +30585,8 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas
converter: new ValueConverter(
(string v) => (long)StringEnumConverter.ConvertToEnum(v),
(long value) => ((CSharpRuntimeModelCodeGeneratorTest.EnumU32)value).ToString()),
- jsonValueReaderWriter: new JsonConvertedValueReaderWriter(
- new JsonConvertedValueReaderWriter(
- JsonInt64ReaderWriter.Instance,
- new ValueConverter(
- (CSharpRuntimeModelCodeGeneratorTest.EnumU32 value) => (long)value,
- (long value) => (CSharpRuntimeModelCodeGeneratorTest.EnumU32)value)),
+ jsonValueReaderWriter: new JsonConvertedValueReaderWriter(
+ JsonInt64ReaderWriter.Instance,
new ValueConverter(
(string v) => (long)StringEnumConverter.ConvertToEnum(v),
(long value) => ((CSharpRuntimeModelCodeGeneratorTest.EnumU32)value).ToString())));
@@ -30760,12 +30740,8 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas
(string v) => new Uri(v, UriKind.RelativeOrAbsolute).ToString(),
(string v) => new Uri(v, UriKind.RelativeOrAbsolute).ToString()),
storeTypePostfix: StoreTypePostfix.None,
- jsonValueReaderWriter: new JsonConvertedValueReaderWriter(
- new JsonConvertedValueReaderWriter(
- JsonStringReaderWriter.Instance,
- new ValueConverter(
- (Uri v) => v.ToString(),
- (string v) => new Uri(v, UriKind.RelativeOrAbsolute))),
+ jsonValueReaderWriter: new JsonConvertedValueReaderWriter(
+ JsonStringReaderWriter.Instance,
new ValueConverter(
(string v) => new Uri(v, UriKind.RelativeOrAbsolute).ToString(),
(string v) => new Uri(v, UriKind.RelativeOrAbsolute).ToString())));
@@ -54045,12 +54021,8 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas
converter: new ValueConverter(
(string v) => (uint)StringEnumConverter.ConvertToEnum(v),
(uint value) => ((CSharpRuntimeModelCodeGeneratorTest.EnumU32)value).ToString()),
- jsonValueReaderWriter: new JsonConvertedValueReaderWriter(
- new JsonConvertedValueReaderWriter(
- JsonUInt32ReaderWriter.Instance,
- new ValueConverter(
- (CSharpRuntimeModelCodeGeneratorTest.EnumU32 value) => (uint)value,
- (uint value) => (CSharpRuntimeModelCodeGeneratorTest.EnumU32)value)),
+ jsonValueReaderWriter: new JsonConvertedValueReaderWriter(
+ JsonUInt32ReaderWriter.Instance,
new ValueConverter(
(string v) => (uint)StringEnumConverter.ConvertToEnum(v),
(uint value) => ((CSharpRuntimeModelCodeGeneratorTest.EnumU32)value).ToString())));
@@ -54181,12 +54153,8 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas
converter: new ValueConverter(
(string v) => new Uri(v, UriKind.RelativeOrAbsolute).ToString(),
(string v) => new Uri(v, UriKind.RelativeOrAbsolute).ToString()),
- jsonValueReaderWriter: new JsonConvertedValueReaderWriter(
- new JsonConvertedValueReaderWriter(
- JsonStringReaderWriter.Instance,
- new ValueConverter(
- (Uri v) => v.ToString(),
- (string v) => new Uri(v, UriKind.RelativeOrAbsolute))),
+ jsonValueReaderWriter: new JsonConvertedValueReaderWriter(
+ JsonStringReaderWriter.Instance,
new ValueConverter(
(string v) => new Uri(v, UriKind.RelativeOrAbsolute).ToString(),
(string v) => new Uri(v, UriKind.RelativeOrAbsolute).ToString())));
diff --git a/test/EFCore.Specification.Tests/JsonTypesTestBase.cs b/test/EFCore.Specification.Tests/JsonTypesTestBase.cs
index c05a4703e7f..dbace97e913 100644
--- a/test/EFCore.Specification.Tests/JsonTypesTestBase.cs
+++ b/test/EFCore.Specification.Tests/JsonTypesTestBase.cs
@@ -1667,6 +1667,72 @@ protected class NullableDddIdType
public DddId? DddId { get; set; }
}
+ [ConditionalTheory]
+ [InlineData(EnumProperty.FieldA, """{"Prop":"A"}""")]
+ [InlineData(EnumProperty.FieldB, """{"Prop":"B"}""")]
+ public virtual void Can_read_write_enum_char_converted_type_JSON_values(int value, string json)
+ => Can_read_and_write_JSON_value(
+ b => b.Entity().HasNoKey().Property(e => e.EnumProperty),
+ b => b.Properties().HaveConversion>(),
+ nameof(EnumCharType.EnumProperty),
+ (EnumProperty)value,
+ json);
+
+ protected class EnumValueConverter() : ValueConverter(
+ p => p.ToChar(null), p => (T)Enum.Parse(typeof(T), Convert.ToInt32(p).ToString()))
+ where T : Enum, IConvertible;
+
+ protected class EnumCharType
+ {
+ public EnumProperty EnumProperty { get; set; }
+ }
+
+ protected enum EnumProperty
+ {
+ FieldA = 'A',
+ FieldB = 'B',
+ FieldC = 'C',
+ }
+
+ [ConditionalTheory]
+ [InlineData("127.0.0.1", """{"Prop":"127.0.0.1"}""")]
+ [InlineData("0.0.0.0", """{"Prop":"0.0.0.0"}""")]
+ [InlineData("255.255.255.255", """{"Prop":"255.255.255.255"}""")]
+ [InlineData("192.168.1.156", """{"Prop":"192.168.1.156"}""")]
+ [InlineData("::1", """{"Prop":"::1"}""")]
+ [InlineData("::", """{"Prop":"::"}""")]
+ [InlineData("2a00:23c7:c60f:4f01:ba43:6d5a:e648:7577", """{"Prop":"2a00:23c7:c60f:4f01:ba43:6d5a:e648:7577"}""")]
+ public virtual void Can_read_write_custom_converted_type_JSON_values(string value, string json)
+ => Can_read_and_write_JSON_value(
+ b => b.Entity().HasNoKey().Property(e => e.Address),
+ b => b.Properties().HaveConversion(),
+ nameof(IpAddressType.Address),
+ new(IPAddress.Parse(value)),
+ json);
+
+ protected class IpAddressConverter() : ValueConverter(
+ v => v.Address,
+ v => new IpAddress(v));
+
+ protected class IpAddressType
+ {
+ public IpAddress? Address { get; set; }
+ }
+
+ protected class IpAddress(IPAddress address)
+ {
+ public IPAddress Address { get; } = address;
+
+ protected bool Equals(IpAddress other)
+ => Address.Equals(other.Address);
+
+ public override bool Equals(object? obj)
+ => !ReferenceEquals(null, obj) && (ReferenceEquals(this, obj) || obj.GetType() == GetType() && Equals((IpAddress)obj));
+
+ public override int GetHashCode()
+ => Address.GetHashCode();
+ }
+
[ConditionalFact]
public virtual void Can_read_write_collection_of_sbyte_JSON_values()
=> Can_read_and_write_JSON_value>(