diff --git a/src/EFCore.Cosmos/Storage/Internal/CosmosTypeMapping.cs b/src/EFCore.Cosmos/Storage/Internal/CosmosTypeMapping.cs
index 8d89854cb4e..7eadf9f059d 100644
--- a/src/EFCore.Cosmos/Storage/Internal/CosmosTypeMapping.cs
+++ b/src/EFCore.Cosmos/Storage/Internal/CosmosTypeMapping.cs
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.EntityFrameworkCore.Storage.Json;
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal;
///
@@ -20,13 +22,15 @@ public class CosmosTypeMapping : CoreTypeMapping
public CosmosTypeMapping(
Type clrType,
ValueComparer? comparer = null,
- ValueComparer? keyComparer = null)
+ ValueComparer? keyComparer = null,
+ JsonValueReaderWriter? jsonValueReaderWriter = null)
: base(
new CoreTypeMappingParameters(
clrType,
converter: null,
comparer,
- keyComparer))
+ keyComparer,
+ jsonValueReaderWriter: jsonValueReaderWriter))
{
}
diff --git a/src/EFCore.Cosmos/Storage/Internal/CosmosTypeMappingSource.cs b/src/EFCore.Cosmos/Storage/Internal/CosmosTypeMappingSource.cs
index f849b45c42d..f555011281c 100644
--- a/src/EFCore.Cosmos/Storage/Internal/CosmosTypeMappingSource.cs
+++ b/src/EFCore.Cosmos/Storage/Internal/CosmosTypeMappingSource.cs
@@ -26,7 +26,13 @@ public CosmosTypeMappingSource(TypeMappingSourceDependencies dependencies)
: base(dependencies)
{
_clrTypeMappings
- = new Dictionary { { typeof(JObject), new CosmosTypeMapping(typeof(JObject)) } };
+ = new Dictionary
+ {
+ {
+ typeof(JObject), new CosmosTypeMapping(
+ typeof(JObject), jsonValueReaderWriter: dependencies.JsonValueReaderWriterSource.FindReaderWriter(typeof(JObject)))
+ }
+ };
}
///
@@ -47,7 +53,7 @@ public CosmosTypeMappingSource(TypeMappingSourceDependencies dependencies)
?? base.FindMapping(mappingInfo));
}
- private static CoreTypeMapping? FindPrimitiveMapping(in TypeMappingInfo mappingInfo)
+ private CoreTypeMapping? FindPrimitiveMapping(in TypeMappingInfo mappingInfo)
{
var clrType = mappingInfo.ClrType!;
if ((clrType.IsValueType
@@ -55,13 +61,14 @@ public CosmosTypeMappingSource(TypeMappingSourceDependencies dependencies)
&& !clrType.IsEnum)
|| clrType == typeof(string))
{
- return new CosmosTypeMapping(clrType);
+ return new CosmosTypeMapping(
+ clrType, jsonValueReaderWriter: Dependencies.JsonValueReaderWriterSource.FindReaderWriter(clrType));
}
return null;
}
- private static CoreTypeMapping? FindCollectionMapping(in TypeMappingInfo mappingInfo)
+ private CoreTypeMapping? FindCollectionMapping(in TypeMappingInfo mappingInfo)
{
var clrType = mappingInfo.ClrType!;
var elementType = clrType.TryGetSequenceType();
@@ -70,6 +77,8 @@ public CosmosTypeMappingSource(TypeMappingSourceDependencies dependencies)
return null;
}
+ var jsonValueReaderWriter = Dependencies.JsonValueReaderWriterSource.FindReaderWriter(clrType);
+
if (clrType.IsArray)
{
var elementMappingInfo = new TypeMappingInfo(elementType);
@@ -77,7 +86,8 @@ public CosmosTypeMappingSource(TypeMappingSourceDependencies dependencies)
?? FindCollectionMapping(elementMappingInfo);
return elementMapping == null
? null
- : new CosmosTypeMapping(clrType, CreateArrayComparer(elementMapping, elementType));
+ : new CosmosTypeMapping(
+ clrType, CreateArrayComparer(elementMapping, elementType), jsonValueReaderWriter: jsonValueReaderWriter);
}
if (clrType.IsGenericType
@@ -93,7 +103,8 @@ public CosmosTypeMappingSource(TypeMappingSourceDependencies dependencies)
?? FindCollectionMapping(elementMappingInfo);
return elementMapping == null
? null
- : new CosmosTypeMapping(clrType, CreateListComparer(elementMapping, elementType, clrType));
+ : new CosmosTypeMapping(
+ clrType, CreateListComparer(elementMapping, elementType, clrType), jsonValueReaderWriter: jsonValueReaderWriter);
}
if (genericTypeDefinition == typeof(Dictionary<,>)
@@ -112,7 +123,9 @@ public CosmosTypeMappingSource(TypeMappingSourceDependencies dependencies)
?? FindCollectionMapping(elementMappingInfo);
return elementMapping == null
? null
- : new CosmosTypeMapping(clrType, CreateStringDictionaryComparer(elementMapping, elementType, clrType));
+ : new CosmosTypeMapping(
+ clrType, CreateStringDictionaryComparer(elementMapping, elementType, clrType),
+ jsonValueReaderWriter: jsonValueReaderWriter);
}
}
diff --git a/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs b/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs
index 3c4ccfbd94a..1f9f21fe697 100644
--- a/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs
+++ b/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs
@@ -834,6 +834,31 @@ private void Create(
.Append("()");
}
+ var jsonValueReaderWriterType = (Type?)property[CoreAnnotationNames.JsonValueReaderWriterType];
+ if (jsonValueReaderWriterType != null)
+ {
+ AddNamespace(jsonValueReaderWriterType, parameters.Namespaces);
+
+ var instanceProperty = jsonValueReaderWriterType.GetAnyProperty("Instance");
+ if (instanceProperty != null
+ && instanceProperty.IsStatic()
+ && instanceProperty.GetMethod?.IsPublic == true
+ && jsonValueReaderWriterType.IsAssignableFrom(instanceProperty.PropertyType))
+ {
+ mainBuilder.AppendLine(",")
+ .Append("jsonValueReaderWriter: ")
+ .Append(_code.Reference(jsonValueReaderWriterType))
+ .Append(".Instance");
+ }
+ else
+ {
+ mainBuilder.AppendLine(",")
+ .Append("jsonValueReaderWriter: new ")
+ .Append(_code.Reference(jsonValueReaderWriterType))
+ .Append("()");
+ }
+ }
+
var sentinel = property.Sentinel;
if (sentinel != null)
{
@@ -900,8 +925,9 @@ private void Create(
}
return i == ForeignKey.LongestFkChainAllowedLength
- ? throw new InvalidOperationException(CoreStrings.RelationshipCycle(
- property.DeclaringEntityType.DisplayName(), property.Name, "ValueConverterType"))
+ ? throw new InvalidOperationException(
+ CoreStrings.RelationshipCycle(
+ property.DeclaringEntityType.DisplayName(), property.Name, "ValueConverterType"))
: null;
}
@@ -1474,18 +1500,13 @@ private static void CreateAnnotations(
{
process(
annotatable,
- parameters with
- {
- Annotations = annotatable.GetAnnotations().ToDictionary(a => a.Name, a => a.Value),
- IsRuntime = false
- });
+ parameters with { Annotations = annotatable.GetAnnotations().ToDictionary(a => a.Name, a => a.Value), IsRuntime = false });
process(
annotatable,
parameters with
{
- Annotations = annotatable.GetRuntimeAnnotations().ToDictionary(a => a.Name, a => a.Value),
- IsRuntime = true
+ Annotations = annotatable.GetRuntimeAnnotations().ToDictionary(a => a.Name, a => a.Value), IsRuntime = true
});
}
diff --git a/src/EFCore.InMemory/Storage/Internal/InMemoryTypeMapping.cs b/src/EFCore.InMemory/Storage/Internal/InMemoryTypeMapping.cs
index c4fc21b2160..1ddf5222a25 100644
--- a/src/EFCore.InMemory/Storage/Internal/InMemoryTypeMapping.cs
+++ b/src/EFCore.InMemory/Storage/Internal/InMemoryTypeMapping.cs
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.EntityFrameworkCore.Storage.Json;
+
namespace Microsoft.EntityFrameworkCore.InMemory.Storage.Internal;
///
@@ -20,13 +22,15 @@ public class InMemoryTypeMapping : CoreTypeMapping
public InMemoryTypeMapping(
Type clrType,
ValueComparer? comparer = null,
- ValueComparer? keyComparer = null)
+ ValueComparer? keyComparer = null,
+ JsonValueReaderWriter? jsonValueReaderWriter = null)
: base(
new CoreTypeMappingParameters(
clrType,
converter: null,
comparer,
- keyComparer))
+ keyComparer,
+ jsonValueReaderWriter: jsonValueReaderWriter))
{
}
diff --git a/src/EFCore.InMemory/Storage/Internal/InMemoryTypeMappingSource.cs b/src/EFCore.InMemory/Storage/Internal/InMemoryTypeMappingSource.cs
index b7b2a014637..cff9a52809d 100644
--- a/src/EFCore.InMemory/Storage/Internal/InMemoryTypeMappingSource.cs
+++ b/src/EFCore.InMemory/Storage/Internal/InMemoryTypeMappingSource.cs
@@ -33,11 +33,14 @@ public InMemoryTypeMappingSource(TypeMappingSourceDependencies dependencies)
var clrType = mappingInfo.ClrType;
Check.DebugAssert(clrType != null, "ClrType is null");
+ var jsonValueReaderWriter = Dependencies.JsonValueReaderWriterSource.FindReaderWriter(clrType);
+
if (clrType.IsValueType
|| clrType == typeof(string)
|| clrType == typeof(byte[]))
{
- return new InMemoryTypeMapping(clrType);
+ return new InMemoryTypeMapping(
+ clrType, jsonValueReaderWriter: jsonValueReaderWriter);
}
if (clrType.FullName == "NetTopologySuite.Geometries.Geometry"
@@ -48,7 +51,8 @@ public InMemoryTypeMappingSource(TypeMappingSourceDependencies dependencies)
return new InMemoryTypeMapping(
clrType,
comparer,
- comparer);
+ comparer,
+ jsonValueReaderWriter);
}
return base.FindMapping(mappingInfo);
diff --git a/src/EFCore.Relational/Storage/BoolTypeMapping.cs b/src/EFCore.Relational/Storage/BoolTypeMapping.cs
index 9446e6ee780..75b359affd2 100644
--- a/src/EFCore.Relational/Storage/BoolTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/BoolTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Storage;
@@ -28,7 +29,7 @@ public class BoolTypeMapping : RelationalTypeMapping
public BoolTypeMapping(
string storeType,
DbType? dbType = System.Data.DbType.Boolean)
- : base(storeType, typeof(bool), dbType)
+ : base(storeType, typeof(bool), dbType, jsonValueReaderWriter: JsonBoolReaderWriter.Instance)
{
}
diff --git a/src/EFCore.Relational/Storage/ByteArrayTypeMapping.cs b/src/EFCore.Relational/Storage/ByteArrayTypeMapping.cs
index c83eb5093c8..022e6620ffb 100644
--- a/src/EFCore.Relational/Storage/ByteArrayTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/ByteArrayTypeMapping.cs
@@ -4,6 +4,7 @@
using System.Data;
using System.Globalization;
using System.Text;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Storage;
@@ -35,7 +36,8 @@ public ByteArrayTypeMapping(
: base(
new RelationalTypeMappingParameters(
new CoreTypeMappingParameters(
- typeof(byte[])), storeType, StoreTypePostfix.None, dbType, unicode: false, size))
+ typeof(byte[]), jsonValueReaderWriter: JsonByteArrayReaderWriter.Instance), storeType, StoreTypePostfix.None, dbType,
+ unicode: false, size))
{
}
diff --git a/src/EFCore.Relational/Storage/ByteTypeMapping.cs b/src/EFCore.Relational/Storage/ByteTypeMapping.cs
index 8dd24ace7f9..6a8a4ef9cc4 100644
--- a/src/EFCore.Relational/Storage/ByteTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/ByteTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Storage;
@@ -28,7 +29,7 @@ public class ByteTypeMapping : RelationalTypeMapping
public ByteTypeMapping(
string storeType,
DbType? dbType = System.Data.DbType.Byte)
- : base(storeType, typeof(byte), dbType)
+ : base(storeType, typeof(byte), dbType, jsonValueReaderWriter: JsonByteReaderWriter.Instance)
{
}
diff --git a/src/EFCore.Relational/Storage/CharTypeMapping.cs b/src/EFCore.Relational/Storage/CharTypeMapping.cs
index e57e04b18fa..ec64f623121 100644
--- a/src/EFCore.Relational/Storage/CharTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/CharTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Storage;
@@ -28,7 +29,7 @@ public class CharTypeMapping : RelationalTypeMapping
public CharTypeMapping(
string storeType,
DbType? dbType = System.Data.DbType.String)
- : base(storeType, typeof(char), dbType)
+ : base(storeType, typeof(char), dbType, jsonValueReaderWriter: JsonCharReaderWriter.Instance)
{
}
diff --git a/src/EFCore.Relational/Storage/DateOnlyTypeMapping.cs b/src/EFCore.Relational/Storage/DateOnlyTypeMapping.cs
index 475e755094c..007b7d93fd0 100644
--- a/src/EFCore.Relational/Storage/DateOnlyTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/DateOnlyTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Storage;
@@ -30,7 +31,7 @@ public class DateOnlyTypeMapping : RelationalTypeMapping
public DateOnlyTypeMapping(
string storeType,
DbType? dbType = System.Data.DbType.Date)
- : base(storeType, typeof(DateOnly), dbType)
+ : base(storeType, typeof(DateOnly), dbType, jsonValueReaderWriter: JsonDateOnlyReaderWriter.Instance)
{
}
diff --git a/src/EFCore.Relational/Storage/DateTimeOffsetTypeMapping.cs b/src/EFCore.Relational/Storage/DateTimeOffsetTypeMapping.cs
index 3202cc3f201..2f1a357398a 100644
--- a/src/EFCore.Relational/Storage/DateTimeOffsetTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/DateTimeOffsetTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Storage;
@@ -30,7 +31,7 @@ public class DateTimeOffsetTypeMapping : RelationalTypeMapping
public DateTimeOffsetTypeMapping(
string storeType,
DbType? dbType = System.Data.DbType.DateTimeOffset)
- : base(storeType, typeof(DateTimeOffset), dbType)
+ : base(storeType, typeof(DateTimeOffset), dbType, jsonValueReaderWriter: JsonDateTimeOffsetReaderWriter.Instance)
{
}
diff --git a/src/EFCore.Relational/Storage/DateTimeTypeMapping.cs b/src/EFCore.Relational/Storage/DateTimeTypeMapping.cs
index 66ba3012470..be52f8b1e33 100644
--- a/src/EFCore.Relational/Storage/DateTimeTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/DateTimeTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Storage;
@@ -30,7 +31,7 @@ public class DateTimeTypeMapping : RelationalTypeMapping
public DateTimeTypeMapping(
string storeType,
DbType? dbType = System.Data.DbType.DateTime)
- : base(storeType, typeof(DateTime), dbType)
+ : base(storeType, typeof(DateTime), dbType, jsonValueReaderWriter: JsonDateTimeReaderWriter.Instance)
{
}
diff --git a/src/EFCore.Relational/Storage/DecimalTypeMapping.cs b/src/EFCore.Relational/Storage/DecimalTypeMapping.cs
index f10e531a11a..d4279754135 100644
--- a/src/EFCore.Relational/Storage/DecimalTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/DecimalTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Storage;
@@ -34,7 +35,8 @@ public DecimalTypeMapping(
DbType? dbType = System.Data.DbType.Decimal,
int? precision = null,
int? scale = null)
- : base(storeType, typeof(decimal), dbType, precision: precision, scale: scale)
+ : base(
+ storeType, typeof(decimal), dbType, precision: precision, scale: scale, jsonValueReaderWriter: JsonDecimalReaderWriter.Instance)
{
}
diff --git a/src/EFCore.Relational/Storage/DoubleTypeMapping.cs b/src/EFCore.Relational/Storage/DoubleTypeMapping.cs
index d787b6b3367..837a7dfa225 100644
--- a/src/EFCore.Relational/Storage/DoubleTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/DoubleTypeMapping.cs
@@ -3,6 +3,7 @@
using System.Data;
using System.Globalization;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Storage;
@@ -29,7 +30,7 @@ public class DoubleTypeMapping : RelationalTypeMapping
public DoubleTypeMapping(
string storeType,
DbType? dbType = System.Data.DbType.Double)
- : base(storeType, typeof(double), dbType)
+ : base(storeType, typeof(double), dbType, jsonValueReaderWriter: JsonDoubleReaderWriter.Instance)
{
}
diff --git a/src/EFCore.Relational/Storage/FloatTypeMapping.cs b/src/EFCore.Relational/Storage/FloatTypeMapping.cs
index 496f4ee4d7e..7b6a73255bf 100644
--- a/src/EFCore.Relational/Storage/FloatTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/FloatTypeMapping.cs
@@ -3,6 +3,7 @@
using System.Data;
using System.Globalization;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Storage;
@@ -29,7 +30,7 @@ public class FloatTypeMapping : RelationalTypeMapping
public FloatTypeMapping(
string storeType,
DbType? dbType = System.Data.DbType.Single)
- : base(storeType, typeof(float), dbType)
+ : base(storeType, typeof(float), dbType, jsonValueReaderWriter: JsonFloatReaderWriter.Instance)
{
}
diff --git a/src/EFCore.Relational/Storage/GuidTypeMapping.cs b/src/EFCore.Relational/Storage/GuidTypeMapping.cs
index a3b2bb2e16d..e4deba15f14 100644
--- a/src/EFCore.Relational/Storage/GuidTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/GuidTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Storage;
@@ -28,7 +29,7 @@ public class GuidTypeMapping : RelationalTypeMapping
public GuidTypeMapping(
string storeType,
DbType? dbType = System.Data.DbType.Guid)
- : base(storeType, typeof(Guid), dbType)
+ : base(storeType, typeof(Guid), dbType, jsonValueReaderWriter: JsonGuidReaderWriter.Instance)
{
}
diff --git a/src/EFCore.Relational/Storage/IntTypeMapping.cs b/src/EFCore.Relational/Storage/IntTypeMapping.cs
index 3744e673446..3a2eeab3697 100644
--- a/src/EFCore.Relational/Storage/IntTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/IntTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Storage;
@@ -28,7 +29,7 @@ public class IntTypeMapping : RelationalTypeMapping
public IntTypeMapping(
string storeType,
DbType? dbType = System.Data.DbType.Int32)
- : base(storeType, typeof(int), dbType)
+ : base(storeType, typeof(int), dbType, jsonValueReaderWriter: JsonInt32ReaderWriter.Instance)
{
}
diff --git a/src/EFCore.Relational/Storage/LongTypeMapping.cs b/src/EFCore.Relational/Storage/LongTypeMapping.cs
index e0b03c8cf39..6730c4a4ad1 100644
--- a/src/EFCore.Relational/Storage/LongTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/LongTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Storage;
@@ -28,7 +29,7 @@ public class LongTypeMapping : RelationalTypeMapping
public LongTypeMapping(
string storeType,
DbType? dbType = System.Data.DbType.Int64)
- : base(storeType, typeof(long), dbType)
+ : base(storeType, typeof(long), dbType, jsonValueReaderWriter: JsonInt64ReaderWriter.Instance)
{
}
diff --git a/src/EFCore.Relational/Storage/RelationalGeometryTypeMapping.cs b/src/EFCore.Relational/Storage/RelationalGeometryTypeMapping.cs
index 8d5082b2ccb..7e3aa4f1356 100644
--- a/src/EFCore.Relational/Storage/RelationalGeometryTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/RelationalGeometryTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Storage;
@@ -20,11 +21,13 @@ public abstract class RelationalGeometryTypeMapping : Rela
/// Creates a new instance of the class.
///
/// The converter to use when converting to and from database types.
+ /// Handles reading and writing JSON values for instances of the mapped type.
/// The store type name.
protected RelationalGeometryTypeMapping(
ValueConverter? converter,
+ JsonValueReaderWriter? jsonValueReaderWriter,
string storeType)
- : base(CreateRelationalTypeMappingParameters(storeType))
+ : base(CreateRelationalTypeMappingParameters(storeType, jsonValueReaderWriter))
{
SpatialConverter = converter;
}
@@ -54,7 +57,9 @@ parameters.CoreParameters with
? (ValueComparer)Activator.CreateInstance(typeof(GeometryValueComparer<>).MakeGenericType(providerType))!
: null;
- private static RelationalTypeMappingParameters CreateRelationalTypeMappingParameters(string storeType)
+ private static RelationalTypeMappingParameters CreateRelationalTypeMappingParameters(
+ string storeType,
+ JsonValueReaderWriter? jsonValueReaderWriter)
{
var comparer = new GeometryValueComparer();
@@ -64,7 +69,8 @@ private static RelationalTypeMappingParameters CreateRelationalTypeMappingParame
null,
comparer,
comparer,
- CreateProviderValueComparer(typeof(TGeometry))),
+ CreateProviderValueComparer(typeof(TGeometry)),
+ jsonValueReaderWriter: jsonValueReaderWriter),
storeType);
}
diff --git a/src/EFCore.Relational/Storage/RelationalTypeMapping.cs b/src/EFCore.Relational/Storage/RelationalTypeMapping.cs
index 1ea795e72ea..484428e7be3 100644
--- a/src/EFCore.Relational/Storage/RelationalTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/RelationalTypeMapping.cs
@@ -4,6 +4,7 @@
using System.Collections.Concurrent;
using System.Data;
using System.Globalization;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Storage;
@@ -270,7 +271,7 @@ private static MethodInfo GetDataReaderMethod(string name)
private sealed class NullTypeMapping : RelationalTypeMapping
{
public NullTypeMapping(string storeType)
- : base(storeType, typeof(object))
+ : base(storeType, typeof(object), jsonValueReaderWriter: JsonNullReaderWriter.Instance)
{
}
@@ -316,6 +317,7 @@ static string GetBaseName(string storeType)
/// A value indicating whether the type has fixed length data or not.
/// The precision of data the property is configured to store, or null if no precision is configured.
/// The scale of data the property is configured to store, or null if no scale is configured.
+ /// Handles reading and writing JSON values for instances of the mapped type.
protected RelationalTypeMapping(
string storeType,
Type clrType,
@@ -324,10 +326,12 @@ protected RelationalTypeMapping(
int? size = null,
bool fixedLength = false,
int? precision = null,
- int? scale = null)
+ int? scale = null,
+ JsonValueReaderWriter? jsonValueReaderWriter = null)
: this(
new RelationalTypeMappingParameters(
- new CoreTypeMappingParameters(clrType), storeType, StoreTypePostfix.None, dbType, unicode, size, fixedLength, precision,
+ new CoreTypeMappingParameters(clrType, jsonValueReaderWriter: jsonValueReaderWriter), storeType, StoreTypePostfix.None,
+ dbType, unicode, size, fixedLength, precision,
scale))
{
}
diff --git a/src/EFCore.Relational/Storage/SByteTypeMapping.cs b/src/EFCore.Relational/Storage/SByteTypeMapping.cs
index 78aaecf305d..1cad5258e92 100644
--- a/src/EFCore.Relational/Storage/SByteTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/SByteTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Storage;
@@ -28,7 +29,7 @@ public class SByteTypeMapping : RelationalTypeMapping
public SByteTypeMapping(
string storeType,
DbType? dbType = System.Data.DbType.SByte)
- : base(storeType, typeof(sbyte), dbType)
+ : base(storeType, typeof(sbyte), dbType, jsonValueReaderWriter: JsonSByteReaderWriter.Instance)
{
}
diff --git a/src/EFCore.Relational/Storage/ShortTypeMapping.cs b/src/EFCore.Relational/Storage/ShortTypeMapping.cs
index 286ca3ba2e6..85abe1730ad 100644
--- a/src/EFCore.Relational/Storage/ShortTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/ShortTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Storage;
@@ -28,7 +29,7 @@ public class ShortTypeMapping : RelationalTypeMapping
public ShortTypeMapping(
string storeType,
DbType? dbType = System.Data.DbType.Int16)
- : base(storeType, typeof(short), dbType)
+ : base(storeType, typeof(short), dbType, jsonValueReaderWriter: JsonInt16ReaderWriter.Instance)
{
}
diff --git a/src/EFCore.Relational/Storage/StringTypeMapping.cs b/src/EFCore.Relational/Storage/StringTypeMapping.cs
index dd0cfab8bc5..b04449e51f0 100644
--- a/src/EFCore.Relational/Storage/StringTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/StringTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Storage;
@@ -35,7 +36,8 @@ public StringTypeMapping(
: base(
new RelationalTypeMappingParameters(
new CoreTypeMappingParameters(
- typeof(string)), storeType, StoreTypePostfix.None, dbType, unicode, size))
+ typeof(string), jsonValueReaderWriter: JsonStringReaderWriter.Instance), storeType, StoreTypePostfix.None, dbType,
+ unicode, size))
{
}
diff --git a/src/EFCore.Relational/Storage/TimeOnlyTypeMapping.cs b/src/EFCore.Relational/Storage/TimeOnlyTypeMapping.cs
index f925ddc6327..1e906afdbf4 100644
--- a/src/EFCore.Relational/Storage/TimeOnlyTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/TimeOnlyTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Storage;
@@ -28,7 +29,7 @@ public class TimeOnlyTypeMapping : RelationalTypeMapping
public TimeOnlyTypeMapping(
string storeType,
DbType? dbType = System.Data.DbType.Time)
- : base(storeType, typeof(TimeOnly), dbType)
+ : base(storeType, typeof(TimeOnly), dbType, jsonValueReaderWriter: JsonTimeOnlyReaderWriter.Instance)
{
}
diff --git a/src/EFCore.Relational/Storage/TimeSpanTypeMapping.cs b/src/EFCore.Relational/Storage/TimeSpanTypeMapping.cs
index b57494383c0..128bec8c55c 100644
--- a/src/EFCore.Relational/Storage/TimeSpanTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/TimeSpanTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Storage;
@@ -28,7 +29,7 @@ public class TimeSpanTypeMapping : RelationalTypeMapping
public TimeSpanTypeMapping(
string storeType,
DbType? dbType = System.Data.DbType.Time)
- : base(storeType, typeof(TimeSpan), dbType)
+ : base(storeType, typeof(TimeSpan), dbType, jsonValueReaderWriter: JsonTimeSpanReaderWriter.Instance)
{
}
diff --git a/src/EFCore.Relational/Storage/UIntTypeMapping.cs b/src/EFCore.Relational/Storage/UIntTypeMapping.cs
index 9a4b9b7f13e..9e3f8995967 100644
--- a/src/EFCore.Relational/Storage/UIntTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/UIntTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Storage;
@@ -28,7 +29,7 @@ public class UIntTypeMapping : RelationalTypeMapping
public UIntTypeMapping(
string storeType,
DbType? dbType = System.Data.DbType.UInt32)
- : base(storeType, typeof(uint), dbType)
+ : base(storeType, typeof(uint), dbType, jsonValueReaderWriter: JsonUInt32ReaderWriter.Instance)
{
}
diff --git a/src/EFCore.Relational/Storage/ULongTypeMapping.cs b/src/EFCore.Relational/Storage/ULongTypeMapping.cs
index 868238bb399..e7f8b51e433 100644
--- a/src/EFCore.Relational/Storage/ULongTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/ULongTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Storage;
@@ -28,7 +29,7 @@ public class ULongTypeMapping : RelationalTypeMapping
public ULongTypeMapping(
string storeType,
DbType? dbType = System.Data.DbType.UInt64)
- : base(storeType, typeof(ulong), dbType)
+ : base(storeType, typeof(ulong), dbType, jsonValueReaderWriter: JsonUInt64ReaderWriter.Instance)
{
}
diff --git a/src/EFCore.Relational/Storage/UShortTypeMapping.cs b/src/EFCore.Relational/Storage/UShortTypeMapping.cs
index 040a2e79f0f..bcd48d71035 100644
--- a/src/EFCore.Relational/Storage/UShortTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/UShortTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Storage;
@@ -28,7 +29,7 @@ public class UShortTypeMapping : RelationalTypeMapping
public UShortTypeMapping(
string storeType,
DbType? dbType = System.Data.DbType.UInt16)
- : base(storeType, typeof(ushort), dbType)
+ : base(storeType, typeof(ushort), dbType, jsonValueReaderWriter: JsonUInt16ReaderWriter.Instance)
{
}
diff --git a/src/EFCore.SqlServer.HierarchyId/Storage/Internal/SqlServerHierarchyIdTypeMapping.cs b/src/EFCore.SqlServer.HierarchyId/Storage/Internal/SqlServerHierarchyIdTypeMapping.cs
index 4c8af78e50a..7ddb0514cc4 100644
--- a/src/EFCore.SqlServer.HierarchyId/Storage/Internal/SqlServerHierarchyIdTypeMapping.cs
+++ b/src/EFCore.SqlServer.HierarchyId/Storage/Internal/SqlServerHierarchyIdTypeMapping.cs
@@ -5,6 +5,7 @@
using System.Data.Common;
using System.Linq.Expressions;
using Microsoft.Data.SqlClient;
+using Microsoft.EntityFrameworkCore.SqlServer.Storage.Json;
using Microsoft.EntityFrameworkCore.SqlServer.Storage.ValueConversion.Internal;
using Microsoft.EntityFrameworkCore.Storage;
@@ -20,10 +21,10 @@ public class SqlServerHierarchyIdTypeMapping : RelationalTypeMapping
{
private const string HierarchyIdFormatConst = "hierarchyid::Parse('{0}')";
- private static readonly ConstructorInfo _hierarchyIdConstructor
+ private static readonly ConstructorInfo HierarchyIdConstructor
= typeof(HierarchyId).GetConstructor(new[] { typeof(string) })!;
- private static readonly SqlServerHierarchyIdValueConverter _valueConverter = new();
+ private static readonly SqlServerHierarchyIdValueConverter ValueConverter = new();
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -33,11 +34,12 @@ private static readonly ConstructorInfo _hierarchyIdConstructor
///
public SqlServerHierarchyIdTypeMapping(string storeType)
: this(
- new RelationalTypeMappingParameters(
- new CoreTypeMappingParameters(
- typeof(HierarchyId),
- _valueConverter),
- storeType))
+ new RelationalTypeMappingParameters(
+ new CoreTypeMappingParameters(
+ typeof(HierarchyId),
+ ValueConverter,
+ jsonValueReaderWriter: SqlServerJsonHierarchyIdReaderWriter.Instance),
+ storeType))
{
}
@@ -83,7 +85,7 @@ protected override void ConfigureParameter(DbParameter parameter)
///
public override Expression GenerateCodeLiteral(object value)
=> Expression.New(
- _hierarchyIdConstructor,
+ HierarchyIdConstructor,
Expression.Constant(((HierarchyId)value).ToString()));
///
diff --git a/src/EFCore.SqlServer.HierarchyId/Storage/Internal/SqlServerHierarchyIdTypeMappingSourcePlugin.cs b/src/EFCore.SqlServer.HierarchyId/Storage/Internal/SqlServerHierarchyIdTypeMappingSourcePlugin.cs
index 16a9e604756..56da9f596b9 100644
--- a/src/EFCore.SqlServer.HierarchyId/Storage/Internal/SqlServerHierarchyIdTypeMappingSourcePlugin.cs
+++ b/src/EFCore.SqlServer.HierarchyId/Storage/Internal/SqlServerHierarchyIdTypeMappingSourcePlugin.cs
@@ -35,18 +35,21 @@ public class SqlServerHierarchyIdTypeMappingSourcePlugin : IRelationalTypeMappin
{
return _hierarchyId;
}
- else if (clrType == typeof(SqlHierarchyId))
+
+ if (clrType == typeof(SqlHierarchyId))
{
return _sqlHierarchyId;
}
return null;
}
- else if (clrType == typeof(HierarchyId))
+
+ if (clrType == typeof(HierarchyId))
{
return _hierarchyId;
}
- else if (clrType == typeof(SqlHierarchyId))
+
+ if (clrType == typeof(SqlHierarchyId))
{
return _sqlHierarchyId;
}
diff --git a/src/EFCore.SqlServer.HierarchyId/Storage/Internal/SqlServerSqlHierarchyIdTypeMapping.cs b/src/EFCore.SqlServer.HierarchyId/Storage/Internal/SqlServerSqlHierarchyIdTypeMapping.cs
index a863601b294..9a26f2b6e58 100644
--- a/src/EFCore.SqlServer.HierarchyId/Storage/Internal/SqlServerSqlHierarchyIdTypeMapping.cs
+++ b/src/EFCore.SqlServer.HierarchyId/Storage/Internal/SqlServerSqlHierarchyIdTypeMapping.cs
@@ -3,6 +3,7 @@
using System.Data.SqlTypes;
using System.Linq.Expressions;
+using Microsoft.EntityFrameworkCore.SqlServer.Storage.Json;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.SqlServer.Types;
@@ -18,7 +19,7 @@ public class SqlServerSqlHierarchyIdTypeMapping : RelationalTypeMapping
{
private const string SqlHierarchyIdFormatConst = "hierarchyid::Parse('{0}')";
- private static readonly MethodInfo _sqlHierarchyIdParseMethod
+ private static readonly MethodInfo SqlHierarchyIdParseMethod
= typeof(SqlHierarchyId).GetRuntimeMethod(nameof(SqlHierarchyId.Parse), new[] { typeof(SqlString) })!;
///
@@ -28,7 +29,7 @@ private static readonly MethodInfo _sqlHierarchyIdParseMethod
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public SqlServerSqlHierarchyIdTypeMapping(string storeType)
- : base(storeType, typeof(SqlHierarchyId))
+ : base(storeType, typeof(SqlHierarchyId), jsonValueReaderWriter: SqlServerJsonSqlHierarchyIdReaderWriter.Instance)
{
}
@@ -69,6 +70,6 @@ protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters p
///
public override Expression GenerateCodeLiteral(object value)
=> Expression.Call(
- _sqlHierarchyIdParseMethod,
+ SqlHierarchyIdParseMethod,
Expression.Convert(Expression.Constant(((SqlHierarchyId)value).ToString()), typeof(SqlString)));
}
diff --git a/src/EFCore.SqlServer.HierarchyId/Storage/Json/SqlServerJsonHierarchyIdReaderWriter.cs b/src/EFCore.SqlServer.HierarchyId/Storage/Json/SqlServerJsonHierarchyIdReaderWriter.cs
new file mode 100644
index 00000000000..f63967a751b
--- /dev/null
+++ b/src/EFCore.SqlServer.HierarchyId/Storage/Json/SqlServerJsonHierarchyIdReaderWriter.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text.Json;
+using Microsoft.EntityFrameworkCore.Storage.Json;
+
+namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Json;
+
+///
+/// Reads and writes JSON for values.
+///
+public sealed class SqlServerJsonHierarchyIdReaderWriter : JsonValueReaderWriter
+{
+ ///
+ /// The singleton instance of this stateless reader/writer.
+ ///
+ public static SqlServerJsonHierarchyIdReaderWriter Instance { get; } = new();
+
+ private SqlServerJsonHierarchyIdReaderWriter()
+ {
+ }
+
+ ///
+ public override HierarchyId FromJsonTyped(ref Utf8JsonReaderManager manager)
+ => new(manager.CurrentReader.GetString()!);
+
+ ///
+ public override void ToJsonTyped(Utf8JsonWriter writer, HierarchyId value)
+ => writer.WriteStringValue(value.ToString());
+}
diff --git a/src/EFCore.SqlServer.HierarchyId/Storage/Json/SqlServerJsonSqlHierarchyIdReaderWriter.cs b/src/EFCore.SqlServer.HierarchyId/Storage/Json/SqlServerJsonSqlHierarchyIdReaderWriter.cs
new file mode 100644
index 00000000000..4ae43f1027b
--- /dev/null
+++ b/src/EFCore.SqlServer.HierarchyId/Storage/Json/SqlServerJsonSqlHierarchyIdReaderWriter.cs
@@ -0,0 +1,31 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text.Json;
+using Microsoft.EntityFrameworkCore.Storage.Json;
+using Microsoft.SqlServer.Types;
+
+namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Json;
+
+///
+/// Reads and writes JSON for values.
+///
+public sealed class SqlServerJsonSqlHierarchyIdReaderWriter : JsonValueReaderWriter
+{
+ ///
+ /// The singleton instance of this stateless reader/writer.
+ ///
+ public static SqlServerJsonSqlHierarchyIdReaderWriter Instance { get; } = new();
+
+ private SqlServerJsonSqlHierarchyIdReaderWriter()
+ {
+ }
+
+ ///
+ public override SqlHierarchyId FromJsonTyped(ref Utf8JsonReaderManager manager)
+ => SqlHierarchyId.Parse(manager.CurrentReader.GetString()!);
+
+ ///
+ public override void ToJsonTyped(Utf8JsonWriter writer, SqlHierarchyId value)
+ => writer.WriteStringValue(value.ToString());
+}
diff --git a/src/EFCore.SqlServer.NTS/Storage/Internal/SqlServerGeometryTypeMapping.cs b/src/EFCore.SqlServer.NTS/Storage/Internal/SqlServerGeometryTypeMapping.cs
index 80763882ca2..1ba2732c92c 100644
--- a/src/EFCore.SqlServer.NTS/Storage/Internal/SqlServerGeometryTypeMapping.cs
+++ b/src/EFCore.SqlServer.NTS/Storage/Internal/SqlServerGeometryTypeMapping.cs
@@ -6,6 +6,7 @@
using System.Text;
using JetBrains.Annotations;
using Microsoft.Data.SqlClient;
+using Microsoft.EntityFrameworkCore.SqlServer.Storage.Json;
using Microsoft.EntityFrameworkCore.SqlServer.Storage.ValueConversion.Internal;
using NetTopologySuite.Geometries;
using NetTopologySuite.IO;
@@ -41,6 +42,7 @@ public SqlServerGeometryTypeMapping(NtsGeometryServices geometryServices, string
new GeometryValueConverter(
CreateReader(geometryServices, IsGeography(storeType)),
CreateWriter(IsGeography(storeType))),
+ SqlServerJsonGeometryWktReaderWriter.Instance,
storeType)
{
_isGeography = IsGeography(storeType);
diff --git a/src/EFCore.SqlServer.NTS/Storage/Json/SqlServerJsonGeometryWktReaderWriter.cs b/src/EFCore.SqlServer.NTS/Storage/Json/SqlServerJsonGeometryWktReaderWriter.cs
new file mode 100644
index 00000000000..a1a7d24521f
--- /dev/null
+++ b/src/EFCore.SqlServer.NTS/Storage/Json/SqlServerJsonGeometryWktReaderWriter.cs
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text.Json;
+using Microsoft.EntityFrameworkCore.Storage.Json;
+using NetTopologySuite.Geometries;
+using NetTopologySuite.IO;
+
+namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Json;
+
+///
+/// Reads and writes JSON using the well-known-text format for values.
+///
+public sealed class SqlServerJsonGeometryWktReaderWriter : JsonValueReaderWriter
+{
+ private static readonly WKTReader WktReader = new();
+
+ ///
+ /// The singleton instance of this stateless reader/writer.
+ ///
+ public static SqlServerJsonGeometryWktReaderWriter Instance { get; } = new();
+
+ private SqlServerJsonGeometryWktReaderWriter()
+ {
+ }
+
+ ///
+ public override Geometry FromJsonTyped(ref Utf8JsonReaderManager manager)
+ => WktReader.Read(manager.CurrentReader.GetString());
+
+ ///
+ public override void ToJsonTyped(Utf8JsonWriter writer, Geometry value)
+ => writer.WriteStringValue(value.ToText());
+}
diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerByteArrayTypeMapping.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerByteArrayTypeMapping.cs
index 3b7472a4002..3e6fe74b0dc 100644
--- a/src/EFCore.SqlServer/Storage/Internal/SqlServerByteArrayTypeMapping.cs
+++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerByteArrayTypeMapping.cs
@@ -5,6 +5,7 @@
using System.Globalization;
using System.Text;
using Microsoft.Data.SqlClient;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
@@ -35,7 +36,7 @@ public SqlServerByteArrayTypeMapping(
StoreTypePostfix? storeTypePostfix = null)
: this(
new RelationalTypeMappingParameters(
- new CoreTypeMappingParameters(typeof(byte[]), null, comparer),
+ new CoreTypeMappingParameters(typeof(byte[]), null, comparer, jsonValueReaderWriter: JsonByteArrayReaderWriter.Instance),
storeType ?? (fixedLength ? "binary" : "varbinary"),
storeTypePostfix ?? StoreTypePostfix.Size,
System.Data.DbType.Binary,
diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerDateTimeOffsetTypeMapping.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerDateTimeOffsetTypeMapping.cs
index 6cc2a29dcf8..59fb13e6ab0 100644
--- a/src/EFCore.SqlServer/Storage/Internal/SqlServerDateTimeOffsetTypeMapping.cs
+++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerDateTimeOffsetTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
@@ -39,7 +40,7 @@ public SqlServerDateTimeOffsetTypeMapping(
StoreTypePostfix storeTypePostfix = StoreTypePostfix.Precision)
: base(
new RelationalTypeMappingParameters(
- new CoreTypeMappingParameters(typeof(DateTimeOffset)),
+ new CoreTypeMappingParameters(typeof(DateTimeOffset), jsonValueReaderWriter: JsonDateTimeOffsetReaderWriter.Instance),
storeType,
storeTypePostfix,
dbType))
diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerDateTimeTypeMapping.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerDateTimeTypeMapping.cs
index 9139d297df2..1a01041cfba 100644
--- a/src/EFCore.SqlServer/Storage/Internal/SqlServerDateTimeTypeMapping.cs
+++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerDateTimeTypeMapping.cs
@@ -3,6 +3,7 @@
using System.Data;
using Microsoft.Data.SqlClient;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
@@ -47,7 +48,7 @@ public SqlServerDateTimeTypeMapping(
StoreTypePostfix storeTypePostfix = StoreTypePostfix.Precision)
: this(
new RelationalTypeMappingParameters(
- new CoreTypeMappingParameters(typeof(DateTime)),
+ new CoreTypeMappingParameters(typeof(DateTime), jsonValueReaderWriter: JsonDateTimeReaderWriter.Instance),
storeType,
storeTypePostfix,
dbType),
diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerDecimalTypeMapping.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerDecimalTypeMapping.cs
index 02d3e867b3c..53b62662c91 100644
--- a/src/EFCore.SqlServer/Storage/Internal/SqlServerDecimalTypeMapping.cs
+++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerDecimalTypeMapping.cs
@@ -3,6 +3,7 @@
using System.Data;
using Microsoft.Data.SqlClient;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
@@ -31,7 +32,7 @@ public SqlServerDecimalTypeMapping(
StoreTypePostfix storeTypePostfix = StoreTypePostfix.PrecisionAndScale)
: this(
new RelationalTypeMappingParameters(
- new CoreTypeMappingParameters(typeof(decimal)),
+ new CoreTypeMappingParameters(typeof(decimal), jsonValueReaderWriter: JsonDecimalReaderWriter.Instance),
storeType,
storeTypePostfix,
dbType)
diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerDoubleTypeMapping.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerDoubleTypeMapping.cs
index ebbe44d90ea..893f39ca7b9 100644
--- a/src/EFCore.SqlServer/Storage/Internal/SqlServerDoubleTypeMapping.cs
+++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerDoubleTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
@@ -28,7 +29,7 @@ public SqlServerDoubleTypeMapping(
StoreTypePostfix storeTypePostfix = StoreTypePostfix.Precision)
: base(
new RelationalTypeMappingParameters(
- new CoreTypeMappingParameters(typeof(double)),
+ new CoreTypeMappingParameters(typeof(double), jsonValueReaderWriter: JsonDoubleReaderWriter.Instance),
storeType,
storeTypePostfix,
dbType))
diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerFloatTypeMapping.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerFloatTypeMapping.cs
index ce39416cd7e..a3d1b9a3b3e 100644
--- a/src/EFCore.SqlServer/Storage/Internal/SqlServerFloatTypeMapping.cs
+++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerFloatTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
@@ -25,7 +26,7 @@ public SqlServerFloatTypeMapping(
StoreTypePostfix storeTypePostfix = StoreTypePostfix.Precision)
: base(
new RelationalTypeMappingParameters(
- new CoreTypeMappingParameters(typeof(float)),
+ new CoreTypeMappingParameters(typeof(float), jsonValueReaderWriter: JsonFloatReaderWriter.Instance),
storeType,
storeTypePostfix,
dbType))
diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerLongTypeMapping.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerLongTypeMapping.cs
index 04e089b4df4..5db7e8f286a 100644
--- a/src/EFCore.SqlServer/Storage/Internal/SqlServerLongTypeMapping.cs
+++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerLongTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
@@ -27,7 +28,8 @@ public SqlServerLongTypeMapping(
DbType? dbType = System.Data.DbType.Int64)
: this(
new RelationalTypeMappingParameters(
- new CoreTypeMappingParameters(typeof(long), converter, comparer, providerValueComparer),
+ new CoreTypeMappingParameters(
+ typeof(long), converter, comparer, providerValueComparer, jsonValueReaderWriter: JsonInt64ReaderWriter.Instance),
storeType,
dbType: dbType))
{
diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerStringTypeMapping.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerStringTypeMapping.cs
index 751da51b988..2c254084f07 100644
--- a/src/EFCore.SqlServer/Storage/Internal/SqlServerStringTypeMapping.cs
+++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerStringTypeMapping.cs
@@ -4,6 +4,7 @@
using System.Data;
using System.Text;
using Microsoft.Data.SqlClient;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
@@ -44,7 +45,8 @@ public SqlServerStringTypeMapping(
new CoreTypeMappingParameters(
typeof(string),
comparer: useKeyComparison ? CaseInsensitiveValueComparer : null,
- keyComparer: useKeyComparison ? CaseInsensitiveValueComparer : null),
+ keyComparer: useKeyComparison ? CaseInsensitiveValueComparer : null,
+ jsonValueReaderWriter: JsonStringReaderWriter.Instance),
storeType ?? GetDefaultStoreName(unicode, fixedLength),
storeTypePostfix ?? StoreTypePostfix.Size,
GetDbType(unicode, fixedLength),
diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerTimeOnlyTypeMapping.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerTimeOnlyTypeMapping.cs
index 38da833b5d9..784767199c7 100644
--- a/src/EFCore.SqlServer/Storage/Internal/SqlServerTimeOnlyTypeMapping.cs
+++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerTimeOnlyTypeMapping.cs
@@ -4,6 +4,7 @@
using System.Data;
using System.Globalization;
using Microsoft.Data.SqlClient;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
@@ -32,7 +33,7 @@ public class SqlServerTimeOnlyTypeMapping : TimeOnlyTypeMapping
internal SqlServerTimeOnlyTypeMapping(string storeType, StoreTypePostfix storeTypePostfix = StoreTypePostfix.Precision)
: base(
new RelationalTypeMappingParameters(
- new CoreTypeMappingParameters(typeof(TimeOnly)),
+ new CoreTypeMappingParameters(typeof(TimeOnly), jsonValueReaderWriter: JsonTimeOnlyReaderWriter.Instance),
storeType,
storeTypePostfix,
System.Data.DbType.Time))
diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerTimeSpanTypeMapping.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerTimeSpanTypeMapping.cs
index ea5f8602f46..d3ebfe40acd 100644
--- a/src/EFCore.SqlServer/Storage/Internal/SqlServerTimeSpanTypeMapping.cs
+++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerTimeSpanTypeMapping.cs
@@ -4,6 +4,7 @@
using System.Data;
using System.Globalization;
using Microsoft.Data.SqlClient;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
@@ -41,7 +42,7 @@ public SqlServerTimeSpanTypeMapping(
StoreTypePostfix storeTypePostfix = StoreTypePostfix.Precision)
: base(
new RelationalTypeMappingParameters(
- new CoreTypeMappingParameters(typeof(TimeSpan)),
+ new CoreTypeMappingParameters(typeof(TimeSpan), jsonValueReaderWriter: JsonTimeSpanReaderWriter.Instance),
storeType,
storeTypePostfix,
dbType))
diff --git a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteDateTimeOffsetTypeMapping.cs b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteDateTimeOffsetTypeMapping.cs
index b10edc13967..fb854d4c4ac 100644
--- a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteDateTimeOffsetTypeMapping.cs
+++ b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteDateTimeOffsetTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal;
@@ -24,7 +25,11 @@ public class SqliteDateTimeOffsetTypeMapping : DateTimeOffsetTypeMapping
public SqliteDateTimeOffsetTypeMapping(
string storeType,
DbType? dbType = System.Data.DbType.DateTimeOffset)
- : base(storeType, dbType)
+ : base(
+ new RelationalTypeMappingParameters(
+ new CoreTypeMappingParameters(typeof(DateTimeOffset), jsonValueReaderWriter: JsonDateTimeOffsetReaderWriter.Instance),
+ storeType,
+ dbType: dbType))
{
}
diff --git a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteDateTimeTypeMapping.cs b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteDateTimeTypeMapping.cs
index e2db03b507f..4c3e64324a0 100644
--- a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteDateTimeTypeMapping.cs
+++ b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteDateTimeTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal;
@@ -24,7 +25,11 @@ public class SqliteDateTimeTypeMapping : DateTimeTypeMapping
public SqliteDateTimeTypeMapping(
string storeType,
DbType? dbType = System.Data.DbType.DateTime)
- : base(storeType, dbType)
+ : this(
+ new RelationalTypeMappingParameters(
+ new CoreTypeMappingParameters(typeof(DateTime), jsonValueReaderWriter: JsonDateTimeReaderWriter.Instance),
+ storeType,
+ dbType: dbType))
{
}
diff --git a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteGuidTypeMapping.cs b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteGuidTypeMapping.cs
index ff744145215..92f35bc73e6 100644
--- a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteGuidTypeMapping.cs
+++ b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteGuidTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal;
@@ -22,7 +23,13 @@ public class SqliteGuidTypeMapping : GuidTypeMapping
public SqliteGuidTypeMapping(
string storeType,
DbType? dbType = System.Data.DbType.Guid)
- : base(storeType, dbType)
+ : this(
+ new RelationalTypeMappingParameters(
+ new CoreTypeMappingParameters(
+ typeof(Guid),
+ jsonValueReaderWriter: JsonGuidReaderWriter.Instance),
+ storeType,
+ dbType: dbType))
{
}
diff --git a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteStringTypeMapping.cs b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteStringTypeMapping.cs
index 590417f511f..edbbbfa60a9 100644
--- a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteStringTypeMapping.cs
+++ b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteStringTypeMapping.cs
@@ -3,6 +3,7 @@
using System.Data;
using System.Text;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal;
@@ -26,7 +27,11 @@ public SqliteStringTypeMapping(
DbType? dbType = null,
bool unicode = false,
int? size = null)
- : base(storeType, dbType, unicode, size)
+ : base(
+ new RelationalTypeMappingParameters(
+ new CoreTypeMappingParameters(
+ typeof(string), jsonValueReaderWriter: JsonStringReaderWriter.Instance), storeType, StoreTypePostfix.None, dbType,
+ unicode, size))
{
}
diff --git a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTimeOnlyTypeMapping.cs b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTimeOnlyTypeMapping.cs
index 4350ffd5b1b..a0e18953465 100644
--- a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTimeOnlyTypeMapping.cs
+++ b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTimeOnlyTypeMapping.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal;
@@ -22,7 +23,11 @@ public class SqliteTimeOnlyTypeMapping : TimeOnlyTypeMapping
public SqliteTimeOnlyTypeMapping(
string storeType,
DbType? dbType = System.Data.DbType.Time)
- : base(storeType, dbType)
+ : base(
+ new RelationalTypeMappingParameters(
+ new CoreTypeMappingParameters(typeof(TimeOnly), jsonValueReaderWriter: JsonTimeOnlyReaderWriter.Instance),
+ storeType,
+ dbType: dbType))
{
}
diff --git a/src/EFCore.Sqlite.NTS/Storage/Internal/SqliteGeometryTypeMapping.cs b/src/EFCore.Sqlite.NTS/Storage/Internal/SqliteGeometryTypeMapping.cs
index ff39d1787b6..db6dc144feb 100644
--- a/src/EFCore.Sqlite.NTS/Storage/Internal/SqliteGeometryTypeMapping.cs
+++ b/src/EFCore.Sqlite.NTS/Storage/Internal/SqliteGeometryTypeMapping.cs
@@ -4,6 +4,7 @@
using System.Text;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Sqlite.Internal;
+using Microsoft.EntityFrameworkCore.Sqlite.Storage.Json;
using Microsoft.EntityFrameworkCore.Sqlite.Storage.ValueConversion.Internal;
using NetTopologySuite.Geometries;
using NetTopologySuite.IO;
@@ -31,7 +32,9 @@ private static readonly MethodInfo _getBytes
///
[UsedImplicitly]
public SqliteGeometryTypeMapping(NtsGeometryServices geometryServices, string storeType)
- : base(new GeometryValueConverter(CreateReader(geometryServices), CreateWriter(storeType)), storeType)
+ : base(
+ new GeometryValueConverter(CreateReader(geometryServices), CreateWriter(storeType)),
+ SqliteJsonGeometryWktReaderWriter.Instance, storeType)
{
}
diff --git a/src/EFCore.Sqlite.NTS/Storage/Json/SqliteJsonGeometryWktReaderWriter.cs b/src/EFCore.Sqlite.NTS/Storage/Json/SqliteJsonGeometryWktReaderWriter.cs
new file mode 100644
index 00000000000..790384a5d37
--- /dev/null
+++ b/src/EFCore.Sqlite.NTS/Storage/Json/SqliteJsonGeometryWktReaderWriter.cs
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text.Json;
+using Microsoft.EntityFrameworkCore.Storage.Json;
+using NetTopologySuite.Geometries;
+using NetTopologySuite.IO;
+
+namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.Json;
+
+///
+/// Reads and writes JSON using the well-known-text format for values.
+///
+public sealed class SqliteJsonGeometryWktReaderWriter : JsonValueReaderWriter
+{
+ private static readonly WKTReader WktReader = new();
+
+ ///
+ /// The singleton instance of this stateless reader/writer.
+ ///
+ public static SqliteJsonGeometryWktReaderWriter Instance { get; } = new();
+
+ private SqliteJsonGeometryWktReaderWriter()
+ {
+ }
+
+ ///
+ public override Geometry FromJsonTyped(ref Utf8JsonReaderManager manager)
+ => WktReader.Read(manager.CurrentReader.GetString());
+
+ ///
+ public override void ToJsonTyped(Utf8JsonWriter writer, Geometry value)
+ => writer.WriteStringValue(value.ToText());
+}
diff --git a/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs b/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs
index 067d3d3aa75..ff4b7b79262 100644
--- a/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs
+++ b/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs
@@ -10,6 +10,7 @@
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Microsoft.EntityFrameworkCore.Storage.Internal;
+using Microsoft.EntityFrameworkCore.Storage.Json;
using Microsoft.EntityFrameworkCore.Update.Internal;
using Microsoft.Extensions.Caching.Memory;
@@ -83,6 +84,7 @@ public static readonly IDictionary CoreServices
{ typeof(IEvaluatableExpressionFilter), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(INavigationExpansionExtensibilityHelper), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IExceptionDetector), new ServiceCharacteristics(ServiceLifetime.Singleton) },
+ { typeof(IJsonValueReaderWriterSource), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IProviderConventionSetBuilder), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IConventionSetBuilder), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IDiagnosticsLogger<>), new ServiceCharacteristics(ServiceLifetime.Scoped) },
@@ -303,6 +305,7 @@ public virtual EntityFrameworkServicesBuilder TryAddCoreServices()
TryAdd();
TryAdd();
TryAdd();
+ TryAdd();
TryAdd(
p => p.GetService()?.FindExtension()?.DbContextLogger
@@ -328,6 +331,7 @@ public virtual EntityFrameworkServicesBuilder TryAddCoreServices()
.AddDependencySingleton()
.AddDependencySingleton()
.AddDependencySingleton()
+ .AddDependencySingleton()
.AddDependencyScoped()
.AddDependencyScoped()
.AddDependencyScoped()
diff --git a/src/EFCore/Metadata/Conventions/RuntimeModelConvention.cs b/src/EFCore/Metadata/Conventions/RuntimeModelConvention.cs
index 6ceb5e42a67..2f0a65583ed 100644
--- a/src/EFCore/Metadata/Conventions/RuntimeModelConvention.cs
+++ b/src/EFCore/Metadata/Conventions/RuntimeModelConvention.cs
@@ -356,6 +356,7 @@ private static RuntimeProperty Create(IProperty property, RuntimeEntityType runt
property.GetValueComparer(),
property.GetKeyValueComparer(),
property.GetProviderValueComparer(),
+ property.GetJsonValueReaderWriter(),
property.GetTypeMapping());
///
diff --git a/src/EFCore/Metadata/IConventionProperty.cs b/src/EFCore/Metadata/IConventionProperty.cs
index 5d2e9dd0e79..9cd79384d95 100644
--- a/src/EFCore/Metadata/IConventionProperty.cs
+++ b/src/EFCore/Metadata/IConventionProperty.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics.CodeAnalysis;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Metadata;
@@ -333,7 +334,8 @@ bool IsImplicitlyCreated()
/// Indicates whether the configuration was specified using a data annotation.
/// The configured value.
Type? SetValueGeneratorFactory(
- [DynamicallyAccessedMembers(ValueGeneratorFactory.DynamicallyAccessedMemberTypes)] Type? valueGeneratorFactory,
+ [DynamicallyAccessedMembers(ValueGeneratorFactory.DynamicallyAccessedMemberTypes)]
+ Type? valueGeneratorFactory,
bool fromDataAnnotation = false);
///
@@ -359,7 +361,8 @@ bool IsImplicitlyCreated()
/// Indicates whether the configuration was specified using a data annotation.
/// The configured value.
Type? SetValueConverter(
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type? converterType,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type? converterType,
bool fromDataAnnotation = false);
///
@@ -400,7 +403,8 @@ bool IsImplicitlyCreated()
/// The configured value.
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
Type? SetValueComparer(
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type? comparerType,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type? comparerType,
bool fromDataAnnotation = false);
///
@@ -427,7 +431,8 @@ bool IsImplicitlyCreated()
/// The configured value.
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
Type? SetProviderValueComparer(
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type? comparerType,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type? comparerType,
bool fromDataAnnotation = false);
///
@@ -435,4 +440,21 @@ bool IsImplicitlyCreated()
///
/// The configuration source for .
ConfigurationSource? GetProviderValueComparerConfigurationSource();
+
+ ///
+ /// Sets the type of to use for this property.
+ ///
+ ///
+ /// A type that inherits from , or to use the reader/writer
+ /// from the type mapping.
+ ///
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// The configured value.
+ Type? SetJsonValueReaderWriterType(Type? readerWriterType, bool fromDataAnnotation = false);
+
+ ///
+ /// Returns the configuration source for .
+ ///
+ /// The configuration source for .
+ ConfigurationSource? GetJsonValueReaderWriterTypeConfigurationSource();
}
diff --git a/src/EFCore/Metadata/IMutableProperty.cs b/src/EFCore/Metadata/IMutableProperty.cs
index 1b042624a34..936c2616a91 100644
--- a/src/EFCore/Metadata/IMutableProperty.cs
+++ b/src/EFCore/Metadata/IMutableProperty.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics.CodeAnalysis;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Metadata;
@@ -202,7 +203,8 @@ public interface IMutableProperty : IReadOnlyProperty, IMutablePropertyBase
/// clear any previously set factory.
///
void SetValueGeneratorFactory(
- [DynamicallyAccessedMembers(ValueGeneratorFactory.DynamicallyAccessedMemberTypes)] Type? valueGeneratorFactory);
+ [DynamicallyAccessedMembers(ValueGeneratorFactory.DynamicallyAccessedMemberTypes)]
+ Type? valueGeneratorFactory);
///
/// Sets the custom for this property.
@@ -256,5 +258,15 @@ void SetValueGeneratorFactory(
///
/// A type that derives from , or to remove any previously set comparer.
///
- void SetProviderValueComparer([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type? comparerType);
+ void SetProviderValueComparer(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type? comparerType);
+
+ ///
+ /// Sets the type of to use for this property for this property.
+ ///
+ ///
+ /// A type that derives from , or to use the reader/writer
+ /// from the type mapping.
+ ///
+ void SetJsonValueReaderWriterType(Type? readerWriterType);
}
diff --git a/src/EFCore/Metadata/IReadOnlyProperty.cs b/src/EFCore/Metadata/IReadOnlyProperty.cs
index ff6117c03be..08bbfe85bf8 100644
--- a/src/EFCore/Metadata/IReadOnlyProperty.cs
+++ b/src/EFCore/Metadata/IReadOnlyProperty.cs
@@ -3,6 +3,7 @@
using System.Text;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Metadata;
@@ -68,8 +69,8 @@ CoreTypeMapping GetTypeMapping()
/// then this is the maximum number of characters.
///
///
- /// The maximum length, -1 if the property has no maximum length, or if the maximum length hasn't been
- /// set.
+ /// The maximum length, -1 if the property has no maximum length, or if the maximum length hasn't been
+ /// set.
///
int? GetMaxLength();
@@ -164,6 +165,12 @@ CoreTypeMapping GetTypeMapping()
/// The comparer, or if none has been set.
ValueComparer? GetProviderValueComparer();
+ ///
+ /// Gets the for this property, or if none is set.
+ ///
+ /// The reader/writer, or if none has been set.
+ JsonValueReaderWriter? GetJsonValueReaderWriter();
+
///
/// Finds the first principal property that the given property is constrained by
/// if the given property is part of a foreign key.
diff --git a/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs b/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs
index 3df816e2e66..8062c2725f0 100644
--- a/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs
+++ b/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs
@@ -316,6 +316,14 @@ public static class CoreAnnotationNames
///
public const string AdHocModel = "AdHocModel";
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public const string JsonValueReaderWriterType = "JsonValueReaderWriterType";
+
///
/// 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
@@ -363,6 +371,7 @@ public static class CoreAnnotationNames
AmbiguousField,
DuplicateServiceProperties,
FullChangeTrackingNotificationsRequired,
- AdHocModel
+ AdHocModel,
+ JsonValueReaderWriterType
};
}
diff --git a/src/EFCore/Metadata/Internal/Property.cs b/src/EFCore/Metadata/Internal/Property.cs
index 00032e5b913..bd79039e222 100644
--- a/src/EFCore/Metadata/Internal/Property.cs
+++ b/src/EFCore/Metadata/Internal/Property.cs
@@ -4,6 +4,7 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Internal;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Metadata.Internal;
@@ -585,7 +586,8 @@ public virtual PropertySaveBehavior GetAfterSaveBehavior()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual Type? SetValueGeneratorFactory(
- [DynamicallyAccessedMembers(ValueGeneratorFactory.DynamicallyAccessedMemberTypes)] Type? factoryType,
+ [DynamicallyAccessedMembers(ValueGeneratorFactory.DynamicallyAccessedMemberTypes)]
+ Type? factoryType,
ConfigurationSource configurationSource)
{
if (factoryType != null)
@@ -667,7 +669,8 @@ public virtual PropertySaveBehavior GetAfterSaveBehavior()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual Type? SetValueConverter(
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type? converterType,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type? converterType,
ConfigurationSource configurationSource)
{
ValueConverter? converter = null;
@@ -749,8 +752,9 @@ public virtual PropertySaveBehavior GetAfterSaveBehavior()
}
return i == ForeignKey.LongestFkChainAllowedLength
- ? throw new InvalidOperationException(CoreStrings.RelationshipCycle(
- DeclaringEntityType.DisplayName(), Name, "ValueConverter"))
+ ? throw new InvalidOperationException(
+ CoreStrings.RelationshipCycle(
+ DeclaringEntityType.DisplayName(), Name, "ValueConverter"))
: null;
}
@@ -840,8 +844,9 @@ public virtual PropertySaveBehavior GetAfterSaveBehavior()
}
return i == ForeignKey.LongestFkChainAllowedLength
- ? throw new InvalidOperationException(CoreStrings.RelationshipCycle(
- DeclaringEntityType.DisplayName(), Name, "ProviderClrType"))
+ ? throw new InvalidOperationException(
+ CoreStrings.RelationshipCycle(
+ DeclaringEntityType.DisplayName(), Name, "ProviderClrType"))
: null;
}
@@ -927,7 +932,8 @@ public virtual CoreTypeMapping? TypeMapping
///
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
public virtual Type? SetValueComparer(
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type? comparerType,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type? comparerType,
ConfigurationSource configurationSource)
{
ValueComparer? comparer = null;
@@ -1029,7 +1035,8 @@ public virtual CoreTypeMapping? TypeMapping
///
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
public virtual Type? SetProviderValueComparer(
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type? comparerType,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type? comparerType,
ConfigurationSource configurationSource)
{
ValueComparer? comparer = null;
@@ -1122,6 +1129,74 @@ public virtual CoreTypeMapping? TypeMapping
ClrType.ShortDisplayName())
: null;
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual JsonValueReaderWriter? GetJsonValueReaderWriter()
+ {
+ return TryCreateReader((Type?)this[CoreAnnotationNames.JsonValueReaderWriterType])
+ ?? TypeMapping?.JsonValueReaderWriter;
+
+ static JsonValueReaderWriter? TryCreateReader(Type? readerWriterType)
+ {
+ if (readerWriterType != null)
+ {
+ var instanceProperty = readerWriterType.GetAnyProperty("Instance");
+ try
+ {
+ return instanceProperty != null
+ && instanceProperty.IsStatic()
+ && instanceProperty.GetMethod?.IsPublic == true
+ && readerWriterType.IsAssignableFrom(instanceProperty.PropertyType)
+ ? (JsonValueReaderWriter?)instanceProperty.GetValue(null)
+ : (JsonValueReaderWriter?)Activator.CreateInstance(readerWriterType);
+ }
+ catch (Exception e)
+ {
+ throw new InvalidOperationException(
+ CoreStrings.CannotCreateJsonValueReaderWriter(
+ readerWriterType.ShortDisplayName()), e);
+ }
+ }
+
+ return null;
+ }
+ }
+
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual Type? SetJsonValueReaderWriterType(
+ Type? readerWriterType,
+ ConfigurationSource configurationSource)
+ {
+ if (readerWriterType != null)
+ {
+ var genericType = readerWriterType.GetGenericTypeImplementations(typeof(JsonValueReaderWriter<>)).FirstOrDefault();
+ if (genericType == null)
+ {
+ throw new InvalidOperationException(CoreStrings.BadJsonValueReaderWriterType(readerWriterType.ShortDisplayName()));
+ }
+ }
+
+ return (Type?)SetOrRemoveAnnotation(CoreAnnotationNames.JsonValueReaderWriterType, readerWriterType, configurationSource)?.Value;
+ }
+
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual ConfigurationSource? GetJsonValueReaderWriterTypeConfigurationSource()
+ => FindAnnotation(CoreAnnotationNames.JsonValueReaderWriterType)?.GetConfigurationSource();
+
///
/// 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
@@ -1707,7 +1782,8 @@ void IMutableProperty.SetValueGeneratorFactory(Func
[DebuggerStepThrough]
void IMutableProperty.SetValueGeneratorFactory(
- [DynamicallyAccessedMembers(ValueGeneratorFactory.DynamicallyAccessedMemberTypes)] Type? valueGeneratorFactory)
+ [DynamicallyAccessedMembers(ValueGeneratorFactory.DynamicallyAccessedMemberTypes)]
+ Type? valueGeneratorFactory)
=> SetValueGeneratorFactory(valueGeneratorFactory, ConfigurationSource.Explicit);
///
@@ -1718,7 +1794,8 @@ void IMutableProperty.SetValueGeneratorFactory(
///
[DebuggerStepThrough]
Type? IConventionProperty.SetValueGeneratorFactory(
- [DynamicallyAccessedMembers(ValueGeneratorFactory.DynamicallyAccessedMemberTypes)] Type? valueGeneratorFactory,
+ [DynamicallyAccessedMembers(ValueGeneratorFactory.DynamicallyAccessedMemberTypes)]
+ Type? valueGeneratorFactory,
bool fromDataAnnotation)
=> SetValueGeneratorFactory(
valueGeneratorFactory,
@@ -1753,7 +1830,8 @@ void IMutableProperty.SetValueConverter(ValueConverter? converter)
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
[DebuggerStepThrough]
- void IMutableProperty.SetValueConverter([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type? converterType)
+ void IMutableProperty.SetValueConverter(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type? converterType)
=> SetValueConverter(converterType, ConfigurationSource.Explicit);
///
@@ -1764,7 +1842,8 @@ void IMutableProperty.SetValueConverter([DynamicallyAccessedMembers(DynamicallyA
///
[DebuggerStepThrough]
Type? IConventionProperty.SetValueConverter(
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type? converterType,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type? converterType,
bool fromDataAnnotation)
=> SetValueConverter(
converterType,
@@ -1821,7 +1900,8 @@ void IMutableProperty.SetValueComparer(ValueComparer? comparer)
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
[DebuggerStepThrough]
- void IMutableProperty.SetValueComparer([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type? comparerType)
+ void IMutableProperty.SetValueComparer(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type? comparerType)
=> SetValueComparer(comparerType, ConfigurationSource.Explicit);
///
@@ -1833,7 +1913,8 @@ void IMutableProperty.SetValueComparer([DynamicallyAccessedMembers(DynamicallyAc
[DebuggerStepThrough]
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
Type? IConventionProperty.SetValueComparer(
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type? comparerType,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type? comparerType,
bool fromDataAnnotation)
=> SetValueComparer(
comparerType,
@@ -1888,7 +1969,8 @@ void IMutableProperty.SetProviderValueComparer(ValueComparer? comparer)
///
[DebuggerStepThrough]
void IMutableProperty.SetProviderValueComparer(
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type? comparerType)
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type? comparerType)
=> SetProviderValueComparer(comparerType, ConfigurationSource.Explicit);
///
@@ -1900,7 +1982,8 @@ void IMutableProperty.SetProviderValueComparer(
[DebuggerStepThrough]
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
Type? IConventionProperty.SetProviderValueComparer(
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type? comparerType,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type? comparerType,
bool fromDataAnnotation)
=> SetProviderValueComparer(
comparerType,
@@ -1916,6 +1999,40 @@ void IMutableProperty.SetProviderValueComparer(
ValueComparer IProperty.GetProviderValueComparer()
=> GetProviderValueComparer()!;
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [DebuggerStepThrough]
+ void IMutableProperty.SetJsonValueReaderWriterType(Type? readerWriterType)
+ => SetJsonValueReaderWriterType(readerWriterType, ConfigurationSource.Explicit);
+
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [DebuggerStepThrough]
+ Type? IConventionProperty.SetJsonValueReaderWriterType(
+ Type? readerWriterType,
+ bool fromDataAnnotation)
+ => SetJsonValueReaderWriterType(
+ readerWriterType,
+ fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ /// 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
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [DebuggerStepThrough]
+ JsonValueReaderWriter? IReadOnlyProperty.GetJsonValueReaderWriter()
+ => GetJsonValueReaderWriter();
+
///
/// Gets the sentinel value that indicates that this property is not set.
///
diff --git a/src/EFCore/Metadata/RuntimeEntityType.cs b/src/EFCore/Metadata/RuntimeEntityType.cs
index 863a8e20c03..4aedc345306 100644
--- a/src/EFCore/Metadata/RuntimeEntityType.cs
+++ b/src/EFCore/Metadata/RuntimeEntityType.cs
@@ -5,6 +5,7 @@
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Metadata;
@@ -605,6 +606,7 @@ private IEnumerable GetIndexes()
/// The for this property.
/// The to use with keys for this property.
/// The to use for the provider values for this property.
+ /// The for this property.
/// The for this property.
/// The newly created property.
public virtual RuntimeProperty AddProperty(
@@ -629,6 +631,7 @@ public virtual RuntimeProperty AddProperty(
ValueComparer? valueComparer = null,
ValueComparer? keyValueComparer = null,
ValueComparer? providerValueComparer = null,
+ JsonValueReaderWriter? jsonValueReaderWriter = null,
CoreTypeMapping? typeMapping = null)
{
var property = new RuntimeProperty(
@@ -654,6 +657,7 @@ public virtual RuntimeProperty AddProperty(
valueComparer,
keyValueComparer,
providerValueComparer,
+ jsonValueReaderWriter,
typeMapping);
_properties.Add(property.Name, property);
@@ -727,7 +731,7 @@ private IEnumerable GetProperties()
/// The name of the property to add.
/// The corresponding CLR property or for a shadow property.
/// The corresponding CLR field or for a shadow property.
- /// The type of the service, or to use the type of the member.
+ /// The type of the service, or to use the type of the member.
/// The used for this property.
/// The newly created service property.
public virtual RuntimeServiceProperty AddServiceProperty(
diff --git a/src/EFCore/Metadata/RuntimeProperty.cs b/src/EFCore/Metadata/RuntimeProperty.cs
index 7ecc327ab3e..983b00982c4 100644
--- a/src/EFCore/Metadata/RuntimeProperty.cs
+++ b/src/EFCore/Metadata/RuntimeProperty.cs
@@ -1,10 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using System.Diagnostics.CodeAnalysis;
+using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Metadata;
@@ -27,6 +28,7 @@ public class RuntimeProperty : RuntimePropertyBase, IProperty
private ValueComparer? _valueComparer;
private ValueComparer? _keyValueComparer;
private readonly ValueComparer? _providerValueComparer;
+ private readonly JsonValueReaderWriter? _jsonValueReaderWriter;
private CoreTypeMapping? _typeMapping;
///
@@ -59,6 +61,7 @@ public RuntimeProperty(
ValueComparer? valueComparer,
ValueComparer? keyValueComparer,
ValueComparer? providerValueComparer,
+ JsonValueReaderWriter? jsonValueReaderWriter,
CoreTypeMapping? typeMapping)
: base(name, propertyInfo, fieldInfo, propertyAccessMode)
{
@@ -102,6 +105,7 @@ public RuntimeProperty(
_valueComparer = valueComparer;
_keyValueComparer = keyValueComparer ?? valueComparer;
_providerValueComparer = providerValueComparer;
+ _jsonValueReaderWriter = jsonValueReaderWriter;
}
///
@@ -209,7 +213,7 @@ private ValueComparer GetKeyValueComparer()
private ValueComparer? GetKeyValueComparer(HashSet? checkedProperties)
{
- if ( _keyValueComparer != null)
+ if (_keyValueComparer != null)
{
return _keyValueComparer;
}
@@ -233,6 +237,13 @@ private ValueComparer GetKeyValueComparer()
return principal.GetKeyValueComparer(checkedProperties);
}
+ ///
+ /// Gets the for this property, or if none is set.
+ ///
+ /// The reader/writer, or if none has been set.
+ public virtual JsonValueReaderWriter? GetJsonValueReaderWriter()
+ => _jsonValueReaderWriter;
+
///
public override object? Sentinel
=> _sentinel;
diff --git a/src/EFCore/Properties/CoreStrings.Designer.cs b/src/EFCore/Properties/CoreStrings.Designer.cs
index 356730b18cd..0ede52adaa7 100644
--- a/src/EFCore/Properties/CoreStrings.Designer.cs
+++ b/src/EFCore/Properties/CoreStrings.Designer.cs
@@ -178,6 +178,14 @@ public static string BadFilterOwnedType(object? filter, object? entityType)
GetString("BadFilterOwnedType", nameof(filter), nameof(entityType)),
filter, entityType);
+ ///
+ /// The type '{givenType}' cannot be used as a 'JsonValueReaderWriter' because it does not inherit from the generic 'JsonValueReaderWriter<TValue>'. Make sure to inherit json reader/writers from 'JsonValueReaderWriter<TValue>'.
+ ///
+ public static string BadJsonValueReaderWriterType(object? givenType)
+ => string.Format(
+ GetString("BadJsonValueReaderWriterType", nameof(givenType)),
+ givenType);
+
///
/// The type '{givenType}' cannot be used as a value comparer because it does not inherit from '{expectedType}'. Make sure to inherit value comparers from '{expectedType}'.
///
@@ -232,6 +240,14 @@ public static string CannotConvertEnumValue(object? value, object? enumType)
public static string CannotConvertQueryableToEnumerableMethod
=> GetString("CannotConvertQueryableToEnumerableMethod");
+ ///
+ /// Cannot create an instance of reade/writer type '{readerWriterType}'. Ensure that the type can be instantiated and has a public parameterless constructor, or has a public static 'Instance' field returning the singleton instance to use.
+ ///
+ public static string CannotCreateJsonValueReaderWriter(object? readerWriterType)
+ => string.Format(
+ GetString("CannotCreateJsonValueReaderWriter", nameof(readerWriterType)),
+ readerWriterType);
+
///
/// Cannot create an instance of value comparer type '{generatorType}'. Ensure that the type can be instantiated and has a parameterless constructor, or use the overload of '{method}' that accepts a delegate.
///
diff --git a/src/EFCore/Properties/CoreStrings.resx b/src/EFCore/Properties/CoreStrings.resx
index a1165178f89..6892e37b88a 100644
--- a/src/EFCore/Properties/CoreStrings.resx
+++ b/src/EFCore/Properties/CoreStrings.resx
@@ -1,17 +1,17 @@
-
@@ -174,6 +174,9 @@
The filter expression '{filter}' cannot be specified for owned entity type '{entityType}'. A filter may only be applied to an entity type that is not owned. See https://aka.ms/efcore-docs-owned for more information and examples.
+
+ The type '{givenType}' cannot be used as a 'JsonValueReaderWriter' because it does not inherit from the generic 'JsonValueReaderWriter<TValue>'. Make sure to inherit json reader/writers from 'JsonValueReaderWriter<TValue>'.
+
The type '{givenType}' cannot be used as a value comparer because it does not inherit from '{expectedType}'. Make sure to inherit value comparers from '{expectedType}'.
@@ -195,6 +198,9 @@
Unable to convert a queryable method to an enumerable method. This is likely an issue in Entity Framework, please file an issue at https://go.microsoft.com/fwlink/?linkid=2142044.
+
+ Cannot create an instance of reade/writer type '{readerWriterType}'. Ensure that the type can be instantiated and has a public parameterless constructor, or has a public static 'Instance' field returning the singleton instance to use.
+
Cannot create an instance of value comparer type '{generatorType}'. Ensure that the type can be instantiated and has a parameterless constructor, or use the overload of '{method}' that accepts a delegate.
diff --git a/src/EFCore/Storage/CoreTypeMapping.cs b/src/EFCore/Storage/CoreTypeMapping.cs
index 364aa275bad..a5eb87daeec 100644
--- a/src/EFCore/Storage/CoreTypeMapping.cs
+++ b/src/EFCore/Storage/CoreTypeMapping.cs
@@ -3,6 +3,7 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.EntityFrameworkCore.Internal;
+using Microsoft.EntityFrameworkCore.Storage.Json;
namespace Microsoft.EntityFrameworkCore.Storage;
@@ -38,6 +39,7 @@ protected readonly record struct CoreTypeMappingParameters
///
/// If this type mapping represents a primitive collection, this holds the element's type mapping.
///
+ /// Handles reading and writing JSON values for instances of the mapped type.
public CoreTypeMappingParameters(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type clrType,
ValueConverter? converter = null,
@@ -45,7 +47,8 @@ public CoreTypeMappingParameters(
ValueComparer? keyComparer = null,
ValueComparer? providerValueComparer = null,
Func? valueGeneratorFactory = null,
- CoreTypeMapping? elementTypeMapping = null)
+ CoreTypeMapping? elementTypeMapping = null,
+ JsonValueReaderWriter? jsonValueReaderWriter = null)
{
ClrType = clrType;
Converter = converter;
@@ -54,6 +57,7 @@ public CoreTypeMappingParameters(
ProviderValueComparer = providerValueComparer;
ValueGeneratorFactory = valueGeneratorFactory;
ElementTypeMapping = elementTypeMapping;
+ JsonValueReaderWriter = jsonValueReaderWriter;
}
///
@@ -91,7 +95,12 @@ public CoreTypeMappingParameters(
///
/// If this type mapping represents a primitive collection, this holds the element's type mapping.
///
- public CoreTypeMapping? ElementTypeMapping { get; }
+ public CoreTypeMapping? ElementTypeMapping { get; init; }
+
+ ///
+ /// Handles reading and writing JSON values for instances of the mapped type.
+ ///
+ public JsonValueReaderWriter? JsonValueReaderWriter { get; init; }
///
/// Creates a new parameter object with the given
@@ -107,7 +116,8 @@ public CoreTypeMappingParameters WithComposedConverter(ValueConverter? converter
KeyComparer,
ProviderValueComparer,
ValueGeneratorFactory,
- ElementTypeMapping);
+ ElementTypeMapping,
+ JsonValueReaderWriter);
///
/// Creates a new parameter object with the given
@@ -123,7 +133,24 @@ public CoreTypeMappingParameters WithElementTypeMapping(CoreTypeMapping elementT
KeyComparer,
ProviderValueComparer,
ValueGeneratorFactory,
- elementTypeMapping);
+ elementTypeMapping,
+ JsonValueReaderWriter);
+
+ ///
+ /// Creates a new parameter object with the given JSON reader/writer.
+ ///
+ /// The element type mapping.
+ /// The new parameter object.
+ public CoreTypeMappingParameters WithJsonValueReaderWriter(JsonValueReaderWriter jsonValueReaderWriter)
+ => new(
+ ClrType,
+ Converter,
+ Comparer,
+ KeyComparer,
+ ProviderValueComparer,
+ ValueGeneratorFactory,
+ ElementTypeMapping,
+ jsonValueReaderWriter);
}
private ValueComparer? _comparer;
@@ -184,7 +211,8 @@ protected CoreTypeMapping(CoreTypeMappingParameters parameters)
///
/// Gets the .NET type used in the EF model.
///
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods
+ [DynamicallyAccessedMembers(
+ DynamicallyAccessedMemberTypes.PublicMethods
| DynamicallyAccessedMemberTypes.NonPublicMethods
| DynamicallyAccessedMemberTypes.PublicProperties)]
public virtual Type ClrType { get; }
@@ -257,4 +285,10 @@ public virtual Expression GenerateCodeLiteral(object value)
///
public virtual CoreTypeMapping? ElementTypeMapping
=> Parameters.ElementTypeMapping;
+
+ ///
+ /// Handles reading and writing JSON values for instances of the mapped type.
+ ///
+ public virtual JsonValueReaderWriter? JsonValueReaderWriter
+ => Parameters.JsonValueReaderWriter;
}
diff --git a/src/EFCore/Storage/Json/IJsonValueReaderWriterSource.cs b/src/EFCore/Storage/Json/IJsonValueReaderWriterSource.cs
new file mode 100644
index 00000000000..c5cfb0cb1b2
--- /dev/null
+++ b/src/EFCore/Storage/Json/IJsonValueReaderWriterSource.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore.Storage.Json;
+
+///
+///
+/// Attempts to find a for a given CLR type.
+///
+///
+///
+///
+/// The service lifetime is . This means a single instance
+/// is used by many instances. The implementation must be thread-safe.
+/// This service cannot depend on services registered as .
+///
+///
+/// See Implementation of database providers and extensions
+/// for more information and examples.
+///
+///
+public interface IJsonValueReaderWriterSource
+{
+ ///
+ /// Attempts to find a for a given CLR type.
+ ///
+ /// The type.
+ /// The found , or if none is available.
+ JsonValueReaderWriter? FindReaderWriter(Type type);
+}
diff --git a/src/EFCore/Storage/Json/JsonBoolReaderWriter.cs b/src/EFCore/Storage/Json/JsonBoolReaderWriter.cs
new file mode 100644
index 00000000000..6885f72f19e
--- /dev/null
+++ b/src/EFCore/Storage/Json/JsonBoolReaderWriter.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text.Json;
+
+namespace Microsoft.EntityFrameworkCore.Storage.Json;
+
+///
+/// Reads and writes JSON for values.
+///
+public sealed class JsonBoolReaderWriter : JsonValueReaderWriter
+{
+ ///
+ /// The singleton instance of this stateless reader/writer.
+ ///
+ public static JsonBoolReaderWriter Instance { get; } = new();
+
+ private JsonBoolReaderWriter()
+ {
+ }
+
+ ///
+ public override bool FromJsonTyped(ref Utf8JsonReaderManager manager)
+ => manager.CurrentReader.GetBoolean();
+
+ ///
+ public override void ToJsonTyped(Utf8JsonWriter writer, bool value)
+ => writer.WriteBooleanValue(value);
+}
diff --git a/src/EFCore/Storage/Json/JsonByteArrayReaderWriter.cs b/src/EFCore/Storage/Json/JsonByteArrayReaderWriter.cs
new file mode 100644
index 00000000000..ebeba2777f8
--- /dev/null
+++ b/src/EFCore/Storage/Json/JsonByteArrayReaderWriter.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text.Json;
+
+namespace Microsoft.EntityFrameworkCore.Storage.Json;
+
+///
+/// Reads and writes JSON as base64 for array values.
+///
+public sealed class JsonByteArrayReaderWriter : JsonValueReaderWriter
+{
+ ///
+ /// The singleton instance of this stateless reader/writer.
+ ///
+ public static JsonByteArrayReaderWriter Instance { get; } = new();
+
+ private JsonByteArrayReaderWriter()
+ {
+ }
+
+ ///
+ public override byte[] FromJsonTyped(ref Utf8JsonReaderManager manager)
+ => manager.CurrentReader.GetBytesFromBase64();
+
+ ///
+ public override void ToJsonTyped(Utf8JsonWriter writer, byte[] value)
+ => writer.WriteBase64StringValue(value);
+}
diff --git a/src/EFCore/Storage/Json/JsonByteReaderWriter.cs b/src/EFCore/Storage/Json/JsonByteReaderWriter.cs
new file mode 100644
index 00000000000..7e91c285e8d
--- /dev/null
+++ b/src/EFCore/Storage/Json/JsonByteReaderWriter.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text.Json;
+
+namespace Microsoft.EntityFrameworkCore.Storage.Json;
+
+///
+/// Reads and writes JSON for values.
+///
+public sealed class JsonByteReaderWriter : JsonValueReaderWriter
+{
+ ///
+ /// The singleton instance of this stateless reader/writer.
+ ///
+ public static JsonByteReaderWriter Instance { get; } = new();
+
+ private JsonByteReaderWriter()
+ {
+ }
+
+ ///
+ public override byte FromJsonTyped(ref Utf8JsonReaderManager manager)
+ => manager.CurrentReader.GetByte();
+
+ ///
+ public override void ToJsonTyped(Utf8JsonWriter writer, byte value)
+ => writer.WriteNumberValue(value);
+}
diff --git a/src/EFCore/Storage/Json/JsonCharReaderWriter.cs b/src/EFCore/Storage/Json/JsonCharReaderWriter.cs
new file mode 100644
index 00000000000..d5e515ef71a
--- /dev/null
+++ b/src/EFCore/Storage/Json/JsonCharReaderWriter.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text.Json;
+
+namespace Microsoft.EntityFrameworkCore.Storage.Json;
+
+///
+/// Reads and writes JSON for values.
+///
+public sealed class JsonCharReaderWriter : JsonValueReaderWriter
+{
+ ///
+ /// The singleton instance of this stateless reader/writer.
+ ///
+ public static JsonCharReaderWriter Instance { get; } = new();
+
+ private JsonCharReaderWriter()
+ {
+ }
+
+ ///
+ public override char FromJsonTyped(ref Utf8JsonReaderManager manager)
+ => manager.CurrentReader.GetString()![0];
+
+ ///
+ public override void ToJsonTyped(Utf8JsonWriter writer, char value)
+ => writer.WriteStringValue(value.ToString());
+}
diff --git a/src/EFCore/Storage/Json/JsonDateOnlyReaderWriter.cs b/src/EFCore/Storage/Json/JsonDateOnlyReaderWriter.cs
new file mode 100644
index 00000000000..dd429d6b39b
--- /dev/null
+++ b/src/EFCore/Storage/Json/JsonDateOnlyReaderWriter.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Globalization;
+using System.Text.Json;
+
+namespace Microsoft.EntityFrameworkCore.Storage.Json;
+
+///
+/// Reads and writes JSON for values.
+///
+public sealed class JsonDateOnlyReaderWriter : JsonValueReaderWriter
+{
+ ///
+ /// The singleton instance of this stateless reader/writer.
+ ///
+ public static JsonDateOnlyReaderWriter Instance { get; } = new();
+
+ private JsonDateOnlyReaderWriter()
+ {
+ }
+
+ ///
+ public override DateOnly FromJsonTyped(ref Utf8JsonReaderManager manager)
+ => DateOnly.Parse(manager.CurrentReader.GetString()!, CultureInfo.InvariantCulture);
+
+ ///
+ public override void ToJsonTyped(Utf8JsonWriter writer, DateOnly value)
+ => writer.WriteStringValue(value.ToString("o", CultureInfo.InvariantCulture));
+}
diff --git a/src/EFCore/Storage/Json/JsonDateTimeOffsetReaderWriter.cs b/src/EFCore/Storage/Json/JsonDateTimeOffsetReaderWriter.cs
new file mode 100644
index 00000000000..41ad92faa5c
--- /dev/null
+++ b/src/EFCore/Storage/Json/JsonDateTimeOffsetReaderWriter.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text.Json;
+
+namespace Microsoft.EntityFrameworkCore.Storage.Json;
+
+///
+/// Reads and writes JSON for values.
+///
+public sealed class JsonDateTimeOffsetReaderWriter : JsonValueReaderWriter
+{
+ ///
+ /// The singleton instance of this stateless reader/writer.
+ ///
+ public static JsonDateTimeOffsetReaderWriter Instance { get; } = new();
+
+ private JsonDateTimeOffsetReaderWriter()
+ {
+ }
+
+ ///
+ public override DateTimeOffset FromJsonTyped(ref Utf8JsonReaderManager manager)
+ => manager.CurrentReader.GetDateTimeOffset();
+
+ ///
+ public override void ToJsonTyped(Utf8JsonWriter writer, DateTimeOffset value)
+ => writer.WriteStringValue(value);
+}
diff --git a/src/EFCore/Storage/Json/JsonDateTimeReaderWriter.cs b/src/EFCore/Storage/Json/JsonDateTimeReaderWriter.cs
new file mode 100644
index 00000000000..8e06bfe76fd
--- /dev/null
+++ b/src/EFCore/Storage/Json/JsonDateTimeReaderWriter.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text.Json;
+
+namespace Microsoft.EntityFrameworkCore.Storage.Json;
+
+///
+/// Reads and writes JSON for values.
+///
+public sealed class JsonDateTimeReaderWriter : JsonValueReaderWriter
+{
+ ///
+ /// The singleton instance of this stateless reader/writer.
+ ///
+ public static JsonDateTimeReaderWriter Instance { get; } = new();
+
+ private JsonDateTimeReaderWriter()
+ {
+ }
+
+ ///
+ public override DateTime FromJsonTyped(ref Utf8JsonReaderManager manager)
+ => manager.CurrentReader.GetDateTime();
+
+ ///
+ public override void ToJsonTyped(Utf8JsonWriter writer, DateTime value)
+ => writer.WriteStringValue(value);
+}
diff --git a/src/EFCore/Storage/Json/JsonDecimalReaderWriter.cs b/src/EFCore/Storage/Json/JsonDecimalReaderWriter.cs
new file mode 100644
index 00000000000..d05339dccbc
--- /dev/null
+++ b/src/EFCore/Storage/Json/JsonDecimalReaderWriter.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text.Json;
+
+namespace Microsoft.EntityFrameworkCore.Storage.Json;
+
+///
+/// Reads and writes JSON for values.
+///
+public sealed class JsonDecimalReaderWriter : JsonValueReaderWriter
+{
+ ///
+ /// The singleton instance of this stateless reader/writer.
+ ///
+ public static JsonDecimalReaderWriter Instance { get; } = new();
+
+ private JsonDecimalReaderWriter()
+ {
+ }
+
+ ///
+ public override decimal FromJsonTyped(ref Utf8JsonReaderManager manager)
+ => manager.CurrentReader.GetDecimal();
+
+ ///
+ public override void ToJsonTyped(Utf8JsonWriter writer, decimal value)
+ => writer.WriteNumberValue(value);
+}
diff --git a/src/EFCore/Storage/Json/JsonDoubleReaderWriter.cs b/src/EFCore/Storage/Json/JsonDoubleReaderWriter.cs
new file mode 100644
index 00000000000..38bbc5412d8
--- /dev/null
+++ b/src/EFCore/Storage/Json/JsonDoubleReaderWriter.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text.Json;
+
+namespace Microsoft.EntityFrameworkCore.Storage.Json;
+
+///
+/// Reads and writes JSON for values.
+///
+public sealed class JsonDoubleReaderWriter : JsonValueReaderWriter
+{
+ ///
+ /// The singleton instance of this stateless reader/writer.
+ ///
+ public static JsonDoubleReaderWriter Instance { get; } = new();
+
+ private JsonDoubleReaderWriter()
+ {
+ }
+
+ ///
+ public override double FromJsonTyped(ref Utf8JsonReaderManager manager)
+ => manager.CurrentReader.GetDouble();
+
+ ///
+ public override void ToJsonTyped(Utf8JsonWriter writer, double value)
+ => writer.WriteNumberValue(value);
+}
diff --git a/src/EFCore/Storage/Json/JsonFloatReaderWriter.cs b/src/EFCore/Storage/Json/JsonFloatReaderWriter.cs
new file mode 100644
index 00000000000..2fe32561426
--- /dev/null
+++ b/src/EFCore/Storage/Json/JsonFloatReaderWriter.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text.Json;
+
+namespace Microsoft.EntityFrameworkCore.Storage.Json;
+
+///
+/// Reads and writes JSON for values.
+///
+public sealed class JsonFloatReaderWriter : JsonValueReaderWriter
+{
+ ///
+ /// The singleton instance of this stateless reader/writer.
+ ///
+ public static JsonFloatReaderWriter Instance { get; } = new();
+
+ private JsonFloatReaderWriter()
+ {
+ }
+
+ ///
+ public override float FromJsonTyped(ref Utf8JsonReaderManager manager)
+ => manager.CurrentReader.GetSingle();
+
+ ///
+ public override void ToJsonTyped(Utf8JsonWriter writer, float value)
+ => writer.WriteNumberValue(value);
+}
diff --git a/src/EFCore/Storage/Json/JsonGuidReaderWriter.cs b/src/EFCore/Storage/Json/JsonGuidReaderWriter.cs
new file mode 100644
index 00000000000..de54f133ca6
--- /dev/null
+++ b/src/EFCore/Storage/Json/JsonGuidReaderWriter.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text.Json;
+
+namespace Microsoft.EntityFrameworkCore.Storage.Json;
+
+///
+/// Reads and writes JSON for values.
+///
+public sealed class JsonGuidReaderWriter : JsonValueReaderWriter
+{
+ ///
+ /// The singleton instance of this stateless reader/writer.
+ ///
+ public static JsonGuidReaderWriter Instance { get; } = new();
+
+ private JsonGuidReaderWriter()
+ {
+ }
+
+ ///
+ public override Guid FromJsonTyped(ref Utf8JsonReaderManager manager)
+ => manager.CurrentReader.GetGuid();
+
+ ///
+ public override void ToJsonTyped(Utf8JsonWriter writer, Guid value)
+ => writer.WriteStringValue(value);
+}
diff --git a/src/EFCore/Storage/Json/JsonInt16ReaderWriter.cs b/src/EFCore/Storage/Json/JsonInt16ReaderWriter.cs
new file mode 100644
index 00000000000..4c7a048dbbe
--- /dev/null
+++ b/src/EFCore/Storage/Json/JsonInt16ReaderWriter.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text.Json;
+
+namespace Microsoft.EntityFrameworkCore.Storage.Json;
+
+///
+/// Reads and writes JSON for values.
+///
+public sealed class JsonInt16ReaderWriter : JsonValueReaderWriter
+{
+ ///
+ /// The singleton instance of this stateless reader/writer.
+ ///
+ public static JsonInt16ReaderWriter Instance { get; } = new();
+
+ private JsonInt16ReaderWriter()
+ {
+ }
+
+ ///
+ public override short FromJsonTyped(ref Utf8JsonReaderManager manager)
+ => manager.CurrentReader.GetInt16();
+
+ ///
+ public override void ToJsonTyped(Utf8JsonWriter writer, short value)
+ => writer.WriteNumberValue(value);
+}
diff --git a/src/EFCore/Storage/Json/JsonInt32ReaderWriter.cs b/src/EFCore/Storage/Json/JsonInt32ReaderWriter.cs
new file mode 100644
index 00000000000..49171c8dc49
--- /dev/null
+++ b/src/EFCore/Storage/Json/JsonInt32ReaderWriter.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text.Json;
+
+namespace Microsoft.EntityFrameworkCore.Storage.Json;
+
+///
+/// Reads and writes JSON for values.
+///
+public sealed class JsonInt32ReaderWriter : JsonValueReaderWriter
+{
+ ///
+ /// The singleton instance of this stateless reader/writer.
+ ///
+ public static JsonInt32ReaderWriter Instance { get; } = new();
+
+ private JsonInt32ReaderWriter()
+ {
+ }
+
+ ///
+ public override int FromJsonTyped(ref Utf8JsonReaderManager manager)
+ => manager.CurrentReader.GetInt32();
+
+ ///
+ public override void ToJsonTyped(Utf8JsonWriter writer, int value)
+ => writer.WriteNumberValue(value);
+}
diff --git a/src/EFCore/Storage/Json/JsonInt64ReaderWriter.cs b/src/EFCore/Storage/Json/JsonInt64ReaderWriter.cs
new file mode 100644
index 00000000000..ee6f4b314b8
--- /dev/null
+++ b/src/EFCore/Storage/Json/JsonInt64ReaderWriter.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text.Json;
+
+namespace Microsoft.EntityFrameworkCore.Storage.Json;
+
+///
+/// Reads and writes JSON for values.
+///
+public sealed class JsonInt64ReaderWriter : JsonValueReaderWriter
+{
+ ///
+ /// The singleton instance of this stateless reader/writer.
+ ///
+ public static JsonInt64ReaderWriter Instance { get; } = new();
+
+ private JsonInt64ReaderWriter()
+ {
+ }
+
+ ///
+ public override long FromJsonTyped(ref Utf8JsonReaderManager manager)
+ => manager.CurrentReader.GetInt64();
+
+ ///
+ public override void ToJsonTyped(Utf8JsonWriter writer, long value)
+ => writer.WriteNumberValue(value);
+}
diff --git a/src/EFCore/Storage/Json/JsonNullReaderWriter.cs b/src/EFCore/Storage/Json/JsonNullReaderWriter.cs
new file mode 100644
index 00000000000..8134d322e1f
--- /dev/null
+++ b/src/EFCore/Storage/Json/JsonNullReaderWriter.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text.Json;
+
+namespace Microsoft.EntityFrameworkCore.Storage.Json;
+
+///
+/// Reads and writes JSON for values.
+///
+public sealed class JsonNullReaderWriter : JsonValueReaderWriter