diff --git a/src/EFCore.Cosmos/EFCore.Cosmos.csproj b/src/EFCore.Cosmos/EFCore.Cosmos.csproj
index 7b8ba97c4d3..c2aa422f9e8 100644
--- a/src/EFCore.Cosmos/EFCore.Cosmos.csproj
+++ b/src/EFCore.Cosmos/EFCore.Cosmos.csproj
@@ -29,14 +29,6 @@
-
-
- CosmosStrings.Designer.tt
- True
- True
-
-
-
diff --git a/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs b/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs
index 5a6368395cf..c9e96792025 100644
--- a/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs
+++ b/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs
@@ -105,6 +105,22 @@ public virtual CosmosDbContextOptionsBuilder IdleTcpConnectionTimeout(TimeSpan t
public virtual CosmosDbContextOptionsBuilder GatewayModeMaxConnectionLimit(int connectionLimit)
=> WithOption(e => e.WithGatewayModeMaxConnectionLimit(Check.NotNull(connectionLimit, nameof(connectionLimit))));
+ ///
+ /// Configures an optional JSON serializer. The client will use it to serialize or
+ /// de-serialize user's cosmos request/responses. SDK owned types such as DatabaseProperties
+ /// and ContainerProperties will always use the SDK default serializer.
+ ///
+ /// The JSON serializer.
+ public virtual CosmosDbContextOptionsBuilder Serializer(CosmosSerializer serializer)
+ => WithOption(e => e.WithSerializer(serializer));
+
+ ///
+ /// Configures the optional serializer options.
+ ///
+ /// Provides a way to configure basic serializer settings.
+ public virtual CosmosDbContextOptionsBuilder SerializationOptions(CosmosSerializationOptions serializationOptions)
+ => WithOption(e => e.WithSerializationOptions(serializationOptions));
+
///
/// Configures the maximum number of TCP connections that may be opened to each Cosmos DB back-end.
/// Together with MaxRequestsPerTcpConnection, this setting limits the number of requests that are
diff --git a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs
index 97fcfa28b66..31047fcf0b9 100644
--- a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs
+++ b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs
@@ -9,6 +9,7 @@
using JetBrains.Annotations;
using Microsoft.Azure.Cosmos;
using Microsoft.EntityFrameworkCore.Cosmos.Internal;
+using Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Utilities;
@@ -43,6 +44,8 @@ public class CosmosOptionsExtension : IDbContextOptionsExtension
private int? _maxRequestsPerTcpConnection;
private bool? _enableContentResponseOnWrite;
private DbContextOptionsExtensionInfo? _info;
+ private CosmosSerializer? _serializer;
+ private CosmosSerializationOptions? _serializationOptions;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -77,6 +80,8 @@ protected CosmosOptionsExtension([NotNull] CosmosOptionsExtension copyFrom)
_gatewayModeMaxConnectionLimit = copyFrom._gatewayModeMaxConnectionLimit;
_maxTcpConnectionsPerEndpoint = copyFrom._maxTcpConnectionsPerEndpoint;
_maxRequestsPerTcpConnection = copyFrom._maxRequestsPerTcpConnection;
+ _serializer = copyFrom._serializer;
+ _serializationOptions = copyFrom._serializationOptions;
}
///
@@ -146,6 +151,62 @@ public virtual CosmosOptionsExtension WithAccountKey([CanBeNull] string? account
return clone;
}
+ ///
+ /// 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 CosmosSerializer? Serializer => _serializer;
+
+ ///
+ /// 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 CosmosOptionsExtension WithSerializer([CanBeNull] CosmosSerializer? serializer)
+ {
+ if (serializer is not null && (_serializationOptions != null))
+ {
+ throw new InvalidOperationException(CosmosStrings.SerializerOptionsConflictingSerializer);
+ }
+
+ var clone = Clone();
+
+ clone._serializer = serializer ?? new JsonCosmosSerializer();
+
+ return clone;
+ }
+
+ ///
+ /// 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 CosmosSerializationOptions? SerializationOptions => _serializationOptions;
+
+ ///
+ /// 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 CosmosOptionsExtension WithSerializationOptions([CanBeNull] CosmosSerializationOptions? serializationOptions)
+ {
+ if (serializationOptions is not null && (_serializer != null))
+ {
+ throw new InvalidOperationException(CosmosStrings.SerializerOptionsConflictingSerializer);
+ }
+
+ var clone = Clone();
+
+ clone._serializationOptions = serializationOptions;
+
+ return clone;
+ }
+
///
/// 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
@@ -560,6 +621,8 @@ public override long GetServiceProviderHashCode()
hashCode = (hashCode * 131) ^ (Extension._gatewayModeMaxConnectionLimit?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ (Extension._maxTcpConnectionsPerEndpoint?.GetHashCode() ?? 0);
hashCode = (hashCode * 131) ^ (Extension._maxRequestsPerTcpConnection?.GetHashCode() ?? 0);
+ hashCode = (hashCode * 131) ^ (Extension._serializer?.GetHashCode() ?? 0);
+ hashCode = (hashCode * 397) ^ (Extension._serializationOptions?.GetHashCode() ?? 0);
_serviceProviderHash = hashCode;
}
diff --git a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosSingletonOptions.cs b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosSingletonOptions.cs
index 0d2379957cd..4fe321ae03a 100644
--- a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosSingletonOptions.cs
+++ b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosSingletonOptions.cs
@@ -139,6 +139,22 @@ public class CosmosSingletonOptions : ICosmosSingletonOptions
///
public virtual bool? EnableContentResponseOnWrite { get; private set; }
+ ///
+ /// 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 CosmosSerializer? Serializer { get; private set; }
+
+ ///
+ /// 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 CosmosSerializationOptions? SerializationOptions { get; private set; }
+
///
/// 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
@@ -164,6 +180,8 @@ public virtual void Initialize(IDbContextOptions options)
MaxTcpConnectionsPerEndpoint = cosmosOptions.MaxTcpConnectionsPerEndpoint;
MaxRequestsPerTcpConnection = cosmosOptions.MaxRequestsPerTcpConnection;
EnableContentResponseOnWrite = cosmosOptions.EnableContentResponseOnWrite;
+ Serializer = cosmosOptions.Serializer;
+ SerializationOptions = cosmosOptions.SerializationOptions;
}
}
@@ -192,6 +210,8 @@ public virtual void Validate(IDbContextOptions options)
|| MaxTcpConnectionsPerEndpoint != cosmosOptions.MaxTcpConnectionsPerEndpoint
|| MaxRequestsPerTcpConnection != cosmosOptions.MaxRequestsPerTcpConnection
|| EnableContentResponseOnWrite != cosmosOptions.EnableContentResponseOnWrite
+ || Serializer != cosmosOptions.Serializer
+ || SerializationOptions != cosmosOptions.SerializationOptions
))
{
throw new InvalidOperationException(
diff --git a/src/EFCore.Cosmos/Infrastructure/Internal/ICosmosSingletonOptions.cs b/src/EFCore.Cosmos/Infrastructure/Internal/ICosmosSingletonOptions.cs
index c60fec5a946..2932776b7d6 100644
--- a/src/EFCore.Cosmos/Infrastructure/Internal/ICosmosSingletonOptions.cs
+++ b/src/EFCore.Cosmos/Infrastructure/Internal/ICosmosSingletonOptions.cs
@@ -138,5 +138,21 @@ public interface ICosmosSingletonOptions : ISingletonOptions
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
bool? EnableContentResponseOnWrite { get; }
+
+ ///
+ /// 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.
+ ///
+ CosmosSerializer? Serializer { get; }
+
+ ///
+ /// 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.
+ ///
+ CosmosSerializationOptions? SerializationOptions { get; }
}
}
diff --git a/src/EFCore.Cosmos/Properties/CosmosStrings.Designer.cs b/src/EFCore.Cosmos/Properties/CosmosStrings.Designer.cs
index 11db1638d02..fe7a0f1a959 100644
--- a/src/EFCore.Cosmos/Properties/CosmosStrings.Designer.cs
+++ b/src/EFCore.Cosmos/Properties/CosmosStrings.Designer.cs
@@ -290,6 +290,12 @@ public static string UpdateConflict([CanBeNull] object? itemId)
public static string VisitChildrenMustBeOverridden
=> GetString("VisitChildrenMustBeOverridden");
+ ///
+ /// SerializerOptions is not compatible with Serializer. Only one can be set.
+ ///
+ public static string SerializerOptionsConflictingSerializer
+ => GetString("SerializerOptionsConflictingSerializer");
+
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name)!;
diff --git a/src/EFCore.Cosmos/Properties/CosmosStrings.resx b/src/EFCore.Cosmos/Properties/CosmosStrings.resx
index eaa7a3e2b8d..d271b340270 100644
--- a/src/EFCore.Cosmos/Properties/CosmosStrings.resx
+++ b/src/EFCore.Cosmos/Properties/CosmosStrings.resx
@@ -218,6 +218,9 @@
Reversing the ordering is not supported when limit or offset are already applied.
+
+ SerializerOptions is not compatible with Serializer. Only one can be set.
+
The Cosmos database provider does not support transactions.
diff --git a/src/EFCore.Cosmos/Storage/Internal/SingletonCosmosClientWrapper.cs b/src/EFCore.Cosmos/Storage/Internal/SingletonCosmosClientWrapper.cs
index bae924cd229..95578c9f3d0 100644
--- a/src/EFCore.Cosmos/Storage/Internal/SingletonCosmosClientWrapper.cs
+++ b/src/EFCore.Cosmos/Storage/Internal/SingletonCosmosClientWrapper.cs
@@ -45,7 +45,7 @@ public SingletonCosmosClientWrapper([NotNull] ICosmosSingletonOptions options)
_endpoint = options.AccountEndpoint;
_key = options.AccountKey;
_connectionString = options.ConnectionString;
- var configuration = new CosmosClientOptions { ApplicationName = _userAgent, Serializer = new JsonCosmosSerializer() };
+ var configuration = new CosmosClientOptions { ApplicationName = _userAgent };
if (options.Region != null)
{
@@ -97,6 +97,16 @@ public SingletonCosmosClientWrapper([NotNull] ICosmosSingletonOptions options)
configuration.MaxRequestsPerTcpConnection = options.MaxRequestsPerTcpConnection.Value;
}
+ if (options.Serializer != null)
+ {
+ configuration.Serializer = options.Serializer;
+ }
+
+ if (options.SerializationOptions != null)
+ {
+ configuration.SerializerOptions = options.SerializationOptions;
+ }
+
_options = configuration;
}
diff --git a/test/EFCore.Cosmos.FunctionalTests/ConfigPatternsCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/ConfigPatternsCosmosTest.cs
index 91b65a9e005..93d0896cc42 100644
--- a/test/EFCore.Cosmos.FunctionalTests/ConfigPatternsCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/ConfigPatternsCosmosTest.cs
@@ -127,6 +127,34 @@ public async Task Should_throw_if_specified_connection_mode_is_wrong()
});
}
+ [ConditionalFact]
+ public async Task Should_use_serialization_options()
+ {
+ var serializationOptions = new CosmosSerializationOptions
+ {
+ IgnoreNullValues = true
+ };
+
+ await using var testDatabase = CosmosTestStore.CreateInitialized(DatabaseName, o =>
+ {
+ o.SerializationOptions(serializationOptions);
+ });
+ var options = CreateOptions(testDatabase);
+
+ var customer = new Customer { Id = 43 };
+
+ using var context = new CustomerContext(options);
+ context.Database.EnsureCreated();
+
+ context.Add(customer);
+
+ context.SaveChanges();
+
+ var customerEntry = context.Entry(context.Find(customer.Id));
+ var jsonProperty = customerEntry.Property("Name");
+ Assert.Null(jsonProperty);
+ }
+
private DbContextOptions CreateOptions(CosmosTestStore testDatabase)
=> Fixture.AddOptions(testDatabase.AddProviderOptions(new DbContextOptionsBuilder()))
.EnableDetailedErrors()
diff --git a/test/EFCore.Cosmos.Tests/Extensions/CosmosDbContextOptionsExtensionsTests.cs b/test/EFCore.Cosmos.Tests/Extensions/CosmosDbContextOptionsExtensionsTests.cs
index d0f3606b7d6..6021b58fd3a 100644
--- a/test/EFCore.Cosmos.Tests/Extensions/CosmosDbContextOptionsExtensionsTests.cs
+++ b/test/EFCore.Cosmos.Tests/Extensions/CosmosDbContextOptionsExtensionsTests.cs
@@ -5,6 +5,7 @@
using System.Net;
using Microsoft.Azure.Cosmos;
using Microsoft.EntityFrameworkCore.Cosmos.Infrastructure.Internal;
+using Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Xunit;
@@ -44,6 +45,66 @@ public void Can_create_options_with_specified_region()
Assert.Equal(regionName, extension.Region);
}
+ [ConditionalFact]
+ public void Can_create_options_with_specified_serializer()
+ {
+ var serializer = new JsonCosmosSerializer();
+ var options = new DbContextOptionsBuilder().UseCosmos(
+ "serviceEndPoint",
+ "authKeyOrResourceToken",
+ "databaseName",
+ o => { o.Serializer(serializer); });
+
+ var extension = options
+ .Options.FindExtension();
+
+ Assert.Same(serializer, extension.Serializer);
+ }
+
+ [ConditionalFact]
+ public void Can_create_options_with_specified_serialization_options()
+ {
+ var serializationOptions = new CosmosSerializationOptions{ IgnoreNullValues = true };
+ var options = new DbContextOptionsBuilder().UseCosmos(
+ "serviceEndPoint",
+ "authKeyOrResourceToken",
+ "databaseName",
+ o => { o.SerializationOptions(serializationOptions); });
+
+ var extension = options
+ .Options.FindExtension();
+
+ Assert.Same(serializationOptions, extension.SerializationOptions);
+ }
+
+ [ConditionalFact]
+ public void Throws_if_specified_serializer_and_serialization_options()
+ {
+ var serializer = new JsonCosmosSerializer();
+ var serializationOptions = new CosmosSerializationOptions { IgnoreNullValues = true };
+ var options = Assert.Throws(
+ () =>
+ new DbContextOptionsBuilder().UseCosmos(
+ "serviceEndPoint",
+ "authKeyOrResourceToken",
+ "databaseName",
+ o => { o.Serializer(serializer).SerializationOptions(serializationOptions); }));
+ }
+
+ [ConditionalFact]
+ public void Throws_if_specified_serialization_options_and_serializer()
+ {
+ var serializationOptions = new CosmosSerializationOptions { IgnoreNullValues = true };
+ var serializer = new JsonCosmosSerializer();
+ var options = Assert.Throws(
+ () =>
+ new DbContextOptionsBuilder().UseCosmos(
+ "serviceEndPoint",
+ "authKeyOrResourceToken",
+ "databaseName",
+ o => { o.SerializationOptions(serializationOptions).Serializer(serializer); }));
+ }
+
[ConditionalFact]
public void Can_create_options_with_wrong_region()
{