Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create correct JSON reader/writer for double nested value conversions #32444

Merged
merged 2 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 25 additions & 9 deletions src/EFCore/Storage/CoreTypeMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -130,15 +131,30 @@ 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 (readerWriter is IJsonConvertedValueReaderWriter convertedValueReaderWriter)
{
readerWriter = convertedValueReaderWriter.InnerReaderWriter;
}

return (JsonValueReaderWriter)Activator.CreateInstance(
typeof(JsonConvertedValueReaderWriter<,>).MakeGenericType(
converter.ModelClrType, readerWriter.ValueType),
readerWriter, converter)!;
}
}
}

Expand Down
66 changes: 66 additions & 0 deletions test/EFCore.Specification.Tests/JsonTypesTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<EnumCharType, EnumProperty>(
b => b.Entity<EnumCharType>().HasNoKey().Property(e => e.EnumProperty),
b => b.Properties<EnumProperty>().HaveConversion<EnumValueConverter<EnumProperty>>(),
nameof(EnumCharType.EnumProperty),
(EnumProperty)value,
json);

protected class EnumValueConverter<T>() : ValueConverter<T, char>(
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<IpAddressType, IpAddress>(
b => b.Entity<IpAddressType>().HasNoKey().Property(e => e.Address),
b => b.Properties<IpAddress>().HaveConversion<IpAddressConverter>(),
nameof(IpAddressType.Address),
new(IPAddress.Parse(value)),
json);

protected class IpAddressConverter() : ValueConverter<IpAddress, IPAddress>(
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<Int8CollectionType, List<sbyte>>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8574,12 +8574,8 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas
converter: new ValueConverter<string, string>(
(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<string, char>(
new JsonConvertedValueReaderWriter<char, string>(
JsonStringReaderWriter.Instance,
new ValueConverter<char, string>(
(char v) => string.Format(CultureInfo.InvariantCulture, "{0}", (object)v),
(string v) => v.Length < 1 ? '\0' : v[0])),
jsonValueReaderWriter: new JsonConvertedValueReaderWriter<string, string>(
JsonStringReaderWriter.Instance,
ajcvickers marked this conversation as resolved.
Show resolved Hide resolved
new ValueConverter<string, string>(
(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])))));
Expand Down Expand Up @@ -8762,12 +8758,8 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas
converter: new ValueConverter<string, long>(
(string v) => (long)StringEnumConverter<string, CompiledModelTestBase.EnumU32, CompiledModelTestBase.EnumU32>.ConvertToEnum(v),
(long value) => ((CompiledModelTestBase.EnumU32)value).ToString()),
jsonValueReaderWriter: new JsonConvertedValueReaderWriter<string, CompiledModelTestBase.EnumU32>(
new JsonConvertedValueReaderWriter<CompiledModelTestBase.EnumU32, long>(
JsonInt64ReaderWriter.Instance,
new ValueConverter<CompiledModelTestBase.EnumU32, long>(
(CompiledModelTestBase.EnumU32 value) => (long)value,
(long value) => (CompiledModelTestBase.EnumU32)value)),
jsonValueReaderWriter: new JsonConvertedValueReaderWriter<string, long>(
JsonInt64ReaderWriter.Instance,
new ValueConverter<string, long>(
(string v) => (long)StringEnumConverter<string, CompiledModelTestBase.EnumU32, CompiledModelTestBase.EnumU32>.ConvertToEnum(v),
(long value) => ((CompiledModelTestBase.EnumU32)value).ToString())));
Expand Down Expand Up @@ -8916,12 +8908,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<string, Uri>(
new JsonConvertedValueReaderWriter<Uri, string>(
JsonStringReaderWriter.Instance,
new ValueConverter<Uri, string>(
(Uri v) => v.ToString(),
(string v) => new Uri(v, UriKind.RelativeOrAbsolute))),
jsonValueReaderWriter: new JsonConvertedValueReaderWriter<string, string>(
JsonStringReaderWriter.Instance,
new ValueConverter<string, string>(
(string v) => new Uri(v, UriKind.RelativeOrAbsolute).ToString(),
(string v) => new Uri(v, UriKind.RelativeOrAbsolute).ToString())));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8574,12 +8574,8 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas
converter: new ValueConverter<string, string>(
(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<string, char>(
new JsonConvertedValueReaderWriter<char, string>(
JsonStringReaderWriter.Instance,
new ValueConverter<char, string>(
(char v) => string.Format(CultureInfo.InvariantCulture, "{0}", (object)v),
(string v) => v.Length < 1 ? '\0' : v[0])),
jsonValueReaderWriter: new JsonConvertedValueReaderWriter<string, string>(
JsonStringReaderWriter.Instance,
new ValueConverter<string, string>(
(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])))));
Expand Down Expand Up @@ -8762,12 +8758,8 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas
converter: new ValueConverter<string, long>(
(string v) => (long)StringEnumConverter<string, CompiledModelTestBase.EnumU32, CompiledModelTestBase.EnumU32>.ConvertToEnum(v),
(long value) => ((CompiledModelTestBase.EnumU32)value).ToString()),
jsonValueReaderWriter: new JsonConvertedValueReaderWriter<string, CompiledModelTestBase.EnumU32>(
new JsonConvertedValueReaderWriter<CompiledModelTestBase.EnumU32, long>(
JsonInt64ReaderWriter.Instance,
new ValueConverter<CompiledModelTestBase.EnumU32, long>(
(CompiledModelTestBase.EnumU32 value) => (long)value,
(long value) => (CompiledModelTestBase.EnumU32)value)),
jsonValueReaderWriter: new JsonConvertedValueReaderWriter<string, long>(
JsonInt64ReaderWriter.Instance,
new ValueConverter<string, long>(
(string v) => (long)StringEnumConverter<string, CompiledModelTestBase.EnumU32, CompiledModelTestBase.EnumU32>.ConvertToEnum(v),
(long value) => ((CompiledModelTestBase.EnumU32)value).ToString())));
Expand Down Expand Up @@ -8916,12 +8908,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<string, Uri>(
new JsonConvertedValueReaderWriter<Uri, string>(
JsonStringReaderWriter.Instance,
new ValueConverter<Uri, string>(
(Uri v) => v.ToString(),
(string v) => new Uri(v, UriKind.RelativeOrAbsolute))),
jsonValueReaderWriter: new JsonConvertedValueReaderWriter<string, string>(
JsonStringReaderWriter.Instance,
new ValueConverter<string, string>(
(string v) => new Uri(v, UriKind.RelativeOrAbsolute).ToString(),
(string v) => new Uri(v, UriKind.RelativeOrAbsolute).ToString())));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7630,12 +7630,8 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas
converter: new ValueConverter<string, uint>(
(string v) => (uint)StringEnumConverter<string, CompiledModelTestBase.EnumU32, CompiledModelTestBase.EnumU32>.ConvertToEnum(v),
(uint value) => ((CompiledModelTestBase.EnumU32)value).ToString()),
jsonValueReaderWriter: new JsonConvertedValueReaderWriter<string, CompiledModelTestBase.EnumU32>(
new JsonConvertedValueReaderWriter<CompiledModelTestBase.EnumU32, uint>(
JsonUInt32ReaderWriter.Instance,
new ValueConverter<CompiledModelTestBase.EnumU32, uint>(
(CompiledModelTestBase.EnumU32 value) => (uint)value,
(uint value) => (CompiledModelTestBase.EnumU32)value)),
jsonValueReaderWriter: new JsonConvertedValueReaderWriter<string, uint>(
JsonUInt32ReaderWriter.Instance,
new ValueConverter<string, uint>(
(string v) => (uint)StringEnumConverter<string, CompiledModelTestBase.EnumU32, CompiledModelTestBase.EnumU32>.ConvertToEnum(v),
(uint value) => ((CompiledModelTestBase.EnumU32)value).ToString())));
Expand Down Expand Up @@ -7761,12 +7757,8 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas
converter: new ValueConverter<string, string>(
(string v) => new Uri(v, UriKind.RelativeOrAbsolute).ToString(),
(string v) => new Uri(v, UriKind.RelativeOrAbsolute).ToString()),
jsonValueReaderWriter: new JsonConvertedValueReaderWriter<string, Uri>(
new JsonConvertedValueReaderWriter<Uri, string>(
JsonStringReaderWriter.Instance,
new ValueConverter<Uri, string>(
(Uri v) => v.ToString(),
(string v) => new Uri(v, UriKind.RelativeOrAbsolute))),
jsonValueReaderWriter: new JsonConvertedValueReaderWriter<string, string>(
JsonStringReaderWriter.Instance,
new ValueConverter<string, string>(
(string v) => new Uri(v, UriKind.RelativeOrAbsolute).ToString(),
(string v) => new Uri(v, UriKind.RelativeOrAbsolute).ToString())));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7630,12 +7630,8 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas
converter: new ValueConverter<string, uint>(
(string v) => (uint)StringEnumConverter<string, CompiledModelTestBase.EnumU32, CompiledModelTestBase.EnumU32>.ConvertToEnum(v),
(uint value) => ((CompiledModelTestBase.EnumU32)value).ToString()),
jsonValueReaderWriter: new JsonConvertedValueReaderWriter<string, CompiledModelTestBase.EnumU32>(
new JsonConvertedValueReaderWriter<CompiledModelTestBase.EnumU32, uint>(
JsonUInt32ReaderWriter.Instance,
new ValueConverter<CompiledModelTestBase.EnumU32, uint>(
(CompiledModelTestBase.EnumU32 value) => (uint)value,
(uint value) => (CompiledModelTestBase.EnumU32)value)),
jsonValueReaderWriter: new JsonConvertedValueReaderWriter<string, uint>(
JsonUInt32ReaderWriter.Instance,
new ValueConverter<string, uint>(
(string v) => (uint)StringEnumConverter<string, CompiledModelTestBase.EnumU32, CompiledModelTestBase.EnumU32>.ConvertToEnum(v),
(uint value) => ((CompiledModelTestBase.EnumU32)value).ToString())));
Expand Down Expand Up @@ -7761,12 +7757,8 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas
converter: new ValueConverter<string, string>(
(string v) => new Uri(v, UriKind.RelativeOrAbsolute).ToString(),
(string v) => new Uri(v, UriKind.RelativeOrAbsolute).ToString()),
jsonValueReaderWriter: new JsonConvertedValueReaderWriter<string, Uri>(
new JsonConvertedValueReaderWriter<Uri, string>(
JsonStringReaderWriter.Instance,
new ValueConverter<Uri, string>(
(Uri v) => v.ToString(),
(string v) => new Uri(v, UriKind.RelativeOrAbsolute))),
jsonValueReaderWriter: new JsonConvertedValueReaderWriter<string, string>(
JsonStringReaderWriter.Instance,
new ValueConverter<string, string>(
(string v) => new Uri(v, UriKind.RelativeOrAbsolute).ToString(),
(string v) => new Uri(v, UriKind.RelativeOrAbsolute).ToString())));
Expand Down
Loading