diff --git a/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs b/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs index c8c42564b38..2e9372d0d7c 100644 --- a/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs +++ b/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs @@ -120,6 +120,15 @@ public virtual CosmosDbContextOptionsBuilder MaxTcpConnectionsPerEndpoint(int co public virtual CosmosDbContextOptionsBuilder MaxRequestsPerTcpConnection(int requestLimit) => WithOption(e => e.WithMaxRequestsPerTcpConnection(Check.NotNull(requestLimit, nameof(requestLimit)))); + /// + /// Sets the boolean to only return the headers and status code in the Cosmos DB response for write item operation + /// like Create, Upsert, Patch and Replace. Setting the option to false will cause the response to have a null resource. + /// This reduces networking and CPU load by not sending the resource back over the network and serializing it on the client. + /// + /// to have null resource + public virtual CosmosDbContextOptionsBuilder ContentResponseOnWriteEnabled(bool enabled = false) + => WithOption(e => e.ContentResponseOnWriteEnabled(Check.NotNull(enabled, nameof(enabled)))); + /// /// Sets an option by cloning the extension used to store the settings. This ensures the builder /// does not modify options that are already in use elsewhere. diff --git a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs index 3309d3bc01a..33e8ed59405 100644 --- a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs +++ b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs @@ -39,6 +39,7 @@ public class CosmosOptionsExtension : IDbContextOptionsExtension private int? _gatewayModeMaxConnectionLimit; private int? _maxTcpConnectionsPerEndpoint; private int? _maxRequestsPerTcpConnection; + private bool? _enableContentResponseOnWrite; private DbContextOptionsExtensionInfo _info; /// @@ -74,6 +75,7 @@ protected CosmosOptionsExtension([NotNull] CosmosOptionsExtension copyFrom) _gatewayModeMaxConnectionLimit = copyFrom._gatewayModeMaxConnectionLimit; _maxTcpConnectionsPerEndpoint = copyFrom._maxTcpConnectionsPerEndpoint; _maxRequestsPerTcpConnection = copyFrom._maxRequestsPerTcpConnection; + _enableContentResponseOnWrite = copyFrom._enableContentResponseOnWrite; } /// @@ -441,6 +443,30 @@ public virtual CosmosOptionsExtension WithMaxRequestsPerTcpConnection(int reques 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 bool? EnableContentResponseOnWrite + => _enableContentResponseOnWrite; + + /// + /// 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 ContentResponseOnWriteEnabled(bool enabled) + { + var clone = Clone(); + + clone._enableContentResponseOnWrite = enabled; + + return clone; + } + /// /// A factory for creating the default , or if none has been /// configured. diff --git a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosSingletonOptions.cs b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosSingletonOptions.cs index d597da75d6b..4dc74bcd955 100644 --- a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosSingletonOptions.cs +++ b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosSingletonOptions.cs @@ -129,6 +129,14 @@ public class CosmosSingletonOptions : ICosmosSingletonOptions /// public virtual int? MaxRequestsPerTcpConnection { 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 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 @@ -153,6 +161,7 @@ public virtual void Initialize(IDbContextOptions options) GatewayModeMaxConnectionLimit = cosmosOptions.GatewayModeMaxConnectionLimit; MaxTcpConnectionsPerEndpoint = cosmosOptions.MaxTcpConnectionsPerEndpoint; MaxRequestsPerTcpConnection = cosmosOptions.MaxRequestsPerTcpConnection; + EnableContentResponseOnWrite = cosmosOptions.EnableContentResponseOnWrite; } } @@ -179,7 +188,9 @@ public virtual void Validate(IDbContextOptions options) || IdleTcpConnectionTimeout != cosmosOptions.IdleTcpConnectionTimeout || GatewayModeMaxConnectionLimit != cosmosOptions.GatewayModeMaxConnectionLimit || MaxTcpConnectionsPerEndpoint != cosmosOptions.MaxTcpConnectionsPerEndpoint - || MaxRequestsPerTcpConnection != cosmosOptions.MaxRequestsPerTcpConnection)) + || MaxRequestsPerTcpConnection != cosmosOptions.MaxRequestsPerTcpConnection + || EnableContentResponseOnWrite != cosmosOptions.EnableContentResponseOnWrite + )) { throw new InvalidOperationException( CoreStrings.SingletonOptionChanged( diff --git a/src/EFCore.Cosmos/Infrastructure/Internal/ICosmosSingletonOptions.cs b/src/EFCore.Cosmos/Infrastructure/Internal/ICosmosSingletonOptions.cs index df14c951248..df2df047861 100644 --- a/src/EFCore.Cosmos/Infrastructure/Internal/ICosmosSingletonOptions.cs +++ b/src/EFCore.Cosmos/Infrastructure/Internal/ICosmosSingletonOptions.cs @@ -128,5 +128,14 @@ public interface ICosmosSingletonOptions : ISingletonOptions /// doing so can result in application failures when updating to a new Entity Framework Core release. /// int? MaxRequestsPerTcpConnection { 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. + /// + bool? EnableContentResponseOnWrite { get; } } } diff --git a/src/EFCore.Cosmos/Storage/Internal/CosmosClientWrapper.cs b/src/EFCore.Cosmos/Storage/Internal/CosmosClientWrapper.cs index 1342182e7a2..c7c4d247814 100644 --- a/src/EFCore.Cosmos/Storage/Internal/CosmosClientWrapper.cs +++ b/src/EFCore.Cosmos/Storage/Internal/CosmosClientWrapper.cs @@ -292,7 +292,8 @@ private async Task CreateItemOnceAsync( var entry = parameters.Entry; var container = Client.GetDatabase(_databaseId).GetContainer(parameters.ContainerId); - var itemRequestOptions = CreateItemRequestOptions(entry); + var enableContentResponseOnWrite = ((ICosmosSingletonOptions)Client.ClientOptions).EnableContentResponseOnWrite; + var itemRequestOptions = CreateItemRequestOptions(entry, enableContentResponseOnWrite); var partitionKey = CreatePartitionKey(entry); using var response = await container.CreateItemStreamAsync(stream, partitionKey, itemRequestOptions, cancellationToken) @@ -354,7 +355,10 @@ private async Task ReplaceItemOnceAsync( var entry = parameters.Entry; var container = Client.GetDatabase(_databaseId).GetContainer(parameters.ContainerId); - var itemRequestOptions = CreateItemRequestOptions(entry); + + var enableContentResponseOnWrite = ((ICosmosSingletonOptions)Client.ClientOptions).EnableContentResponseOnWrite; + var itemRequestOptions = CreateItemRequestOptions(entry, enableContentResponseOnWrite); + var partitionKey = CreatePartitionKey(entry); using var response = await container.ReplaceItemStreamAsync( @@ -416,7 +420,10 @@ public virtual async Task DeleteItemOnceAsync( { var entry = parameters.Entry; var items = Client.GetDatabase(_databaseId).GetContainer(parameters.ContainerId); - var itemRequestOptions = CreateItemRequestOptions(entry); + + var enableContentResponseOnWrite = ((ICosmosSingletonOptions)Client.ClientOptions).EnableContentResponseOnWrite; + + var itemRequestOptions = CreateItemRequestOptions(entry, enableContentResponseOnWrite); var partitionKey = CreatePartitionKey(entry); using var response = await items.DeleteItemStreamAsync( @@ -427,7 +434,7 @@ public virtual async Task DeleteItemOnceAsync( return response.StatusCode == HttpStatusCode.NoContent; } - private static ItemRequestOptions CreateItemRequestOptions(IUpdateEntry entry) + private static ItemRequestOptions CreateItemRequestOptions(IUpdateEntry entry, bool? enableContentResponseOnWrite) { var etagProperty = entry.EntityType.GetETagProperty(); if (etagProperty == null) @@ -442,7 +449,9 @@ private static ItemRequestOptions CreateItemRequestOptions(IUpdateEntry entry) etag = converter.ConvertToProvider(etag); } - return new ItemRequestOptions { IfMatchEtag = (string)etag }; + + + return new ItemRequestOptions { IfMatchEtag = (string)etag, EnableContentResponseOnWrite = enableContentResponseOnWrite }; } private static PartitionKey CreatePartitionKey(IUpdateEntry entry) diff --git a/src/EFCore.Cosmos/Storage/Internal/SingletonCosmosClientWrapper.cs b/src/EFCore.Cosmos/Storage/Internal/SingletonCosmosClientWrapper.cs index 00a33e26fe4..217a0c520d6 100644 --- a/src/EFCore.Cosmos/Storage/Internal/SingletonCosmosClientWrapper.cs +++ b/src/EFCore.Cosmos/Storage/Internal/SingletonCosmosClientWrapper.cs @@ -96,6 +96,11 @@ public SingletonCosmosClientWrapper([NotNull] ICosmosSingletonOptions options) configuration.MaxRequestsPerTcpConnection = options.MaxRequestsPerTcpConnection.Value; } + if (options.EnableContentResponseOnWrite != null) + { + configuration.EnableTcpConnectionEndpointRediscovery = options.EnableContentResponseOnWrite.Value; + } + _options = configuration; } diff --git a/test/EFCore.Cosmos.Tests/Extensions/CosmosDbContextOptionsExtensionsTests.cs b/test/EFCore.Cosmos.Tests/Extensions/CosmosDbContextOptionsExtensionsTests.cs index 311d18b9ada..0030557a2b1 100644 --- a/test/EFCore.Cosmos.Tests/Extensions/CosmosDbContextOptionsExtensionsTests.cs +++ b/test/EFCore.Cosmos.Tests/Extensions/CosmosDbContextOptionsExtensionsTests.cs @@ -191,5 +191,20 @@ public void Can_create_options_with_max_requests_per_tcp_connection() Assert.Equal(requestLimit, extension.MaxRequestsPerTcpConnection); } + + [ConditionalFact] + public void Can_create_options_with_content_response_on_write_enabled() + { + var enabled = true; + var options = new DbContextOptionsBuilder().UseCosmos( + "serviceEndPoint", + "authKeyOrResourceToken", + "databaseName", + o => { o.ContentResponseOnWriteEnabled(enabled); }); + + var extension = options.Options.FindExtension(); + + Assert.Equal(enabled, extension.EnableContentResponseOnWrite); + } } }