diff --git a/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs b/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs index e88fabf78da..7649f106dbb 100644 --- a/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs +++ b/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs @@ -4,6 +4,7 @@ using System.ComponentModel; using System.Net; using Microsoft.EntityFrameworkCore.Cosmos.Infrastructure.Internal; +using static System.Net.WebRequestMethods; namespace Microsoft.EntityFrameworkCore.Infrastructure; @@ -211,6 +212,22 @@ public virtual CosmosDbContextOptionsBuilder MaxRequestsPerTcpConnection(int req public virtual CosmosDbContextOptionsBuilder ContentResponseOnWriteEnabled(bool enabled = true) => WithOption(e => e.ContentResponseOnWriteEnabled(Check.NotNull(enabled))); + /// + /// Sets the boolean to enable the Cosmos DB SDK bulk execution feature. + /// Enabling this feature can improve throughput for small write operations but may increase latency. + /// It is recommended only for high-throughput, non-latency-sensitive scenarios. + /// Because Transactional Batches cannot be executed in bulk mode, + /// any operations batched by EF will not use bulk execution. To ensure operations are executed in bulk, consider disabling batching by setting . + /// For more information, see Saving Data - Azure Cosmos DB Provider. + /// + /// + /// See Using DbContextOptions, and + /// Accessing Azure Cosmos DB with EF Core for more information and examples. + /// + /// to enable the Cosmos DB SDK bulk feature. + public virtual CosmosDbContextOptionsBuilder BulkExecutionEnabled(bool enabled = true) + => WithOption(e => e.BulkExecutionEnabled(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 f2545174ac3..dbc8ac9f993 100644 --- a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs +++ b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs @@ -36,6 +36,7 @@ public class CosmosOptionsExtension : IDbContextOptionsExtension private bool? _enableContentResponseOnWrite; private DbContextOptionsExtensionInfo? _info; private Func? _httpClientFactory; + private bool? _enableBulkExecution; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -564,6 +565,29 @@ public virtual CosmosOptionsExtension WithHttpClientFactory(Func? ht 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? EnableBulkExecution => _enableBulkExecution; + + /// + /// 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 BulkExecutionEnabled(bool enabled) + { + var clone = Clone(); + + clone._enableBulkExecution = enabled; + + 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 @@ -632,6 +656,7 @@ public override int GetServiceProviderHashCode() hashCode.Add(Extension._maxTcpConnectionsPerEndpoint); hashCode.Add(Extension._maxRequestsPerTcpConnection); hashCode.Add(Extension._httpClientFactory); + hashCode.Add(Extension._enableBulkExecution); _serviceProviderHash = hashCode.ToHashCode(); } @@ -656,7 +681,8 @@ public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo && Extension._gatewayModeMaxConnectionLimit == otherInfo.Extension._gatewayModeMaxConnectionLimit && Extension._maxTcpConnectionsPerEndpoint == otherInfo.Extension._maxTcpConnectionsPerEndpoint && Extension._maxRequestsPerTcpConnection == otherInfo.Extension._maxRequestsPerTcpConnection - && Extension._httpClientFactory == otherInfo.Extension._httpClientFactory; + && Extension._httpClientFactory == otherInfo.Extension._httpClientFactory + && Extension._enableBulkExecution == otherInfo.Extension.EnableBulkExecution; public override void PopulateDebugInfo(IDictionary debugInfo) { diff --git a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosSingletonOptions.cs b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosSingletonOptions.cs index af29229cfa5..5fa7ae5a2da 100644 --- a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosSingletonOptions.cs +++ b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosSingletonOptions.cs @@ -151,6 +151,14 @@ public class CosmosSingletonOptions : ICosmosSingletonOptions /// public virtual Func? HttpClientFactory { 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? EnableBulkExecution { 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 @@ -178,6 +186,7 @@ public virtual void Initialize(IDbContextOptions options) MaxTcpConnectionsPerEndpoint = cosmosOptions.MaxTcpConnectionsPerEndpoint; MaxRequestsPerTcpConnection = cosmosOptions.MaxRequestsPerTcpConnection; HttpClientFactory = cosmosOptions.HttpClientFactory; + EnableBulkExecution = cosmosOptions.EnableBulkExecution; } } @@ -208,6 +217,7 @@ public virtual void Validate(IDbContextOptions options) || MaxTcpConnectionsPerEndpoint != cosmosOptions.MaxTcpConnectionsPerEndpoint || MaxRequestsPerTcpConnection != cosmosOptions.MaxRequestsPerTcpConnection || HttpClientFactory != cosmosOptions.HttpClientFactory + || EnableBulkExecution != cosmosOptions.EnableBulkExecution )) { throw new InvalidOperationException( diff --git a/src/EFCore.Cosmos/Infrastructure/Internal/ICosmosSingletonOptions.cs b/src/EFCore.Cosmos/Infrastructure/Internal/ICosmosSingletonOptions.cs index a26b79a82b3..b2d043279fb 100644 --- a/src/EFCore.Cosmos/Infrastructure/Internal/ICosmosSingletonOptions.cs +++ b/src/EFCore.Cosmos/Infrastructure/Internal/ICosmosSingletonOptions.cs @@ -155,4 +155,12 @@ public interface ICosmosSingletonOptions : ISingletonOptions /// doing so can result in application failures when updating to a new Entity Framework Core release. /// Func? HttpClientFactory { 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? EnableBulkExecution { get; } } diff --git a/src/EFCore.Cosmos/Storage/Internal/SingletonCosmosClientWrapper.cs b/src/EFCore.Cosmos/Storage/Internal/SingletonCosmosClientWrapper.cs index 38a820e421d..f52ed9ada70 100644 --- a/src/EFCore.Cosmos/Storage/Internal/SingletonCosmosClientWrapper.cs +++ b/src/EFCore.Cosmos/Storage/Internal/SingletonCosmosClientWrapper.cs @@ -88,6 +88,11 @@ public SingletonCosmosClientWrapper(ICosmosSingletonOptions options) configuration.HttpClientFactory = options.HttpClientFactory; } + if (options.EnableBulkExecution != null) + { + configuration.AllowBulkExecution = options.EnableBulkExecution.Value; + } + _client = options switch { { ConnectionString: not null and not "" } => new CosmosClient(options.ConnectionString, configuration), diff --git a/test/EFCore.Cosmos.Tests/Extensions/CosmosDbContextOptionsExtensionsTests.cs b/test/EFCore.Cosmos.Tests/Extensions/CosmosDbContextOptionsExtensionsTests.cs index 895f45889bf..eb50f35ed8a 100644 --- a/test/EFCore.Cosmos.Tests/Extensions/CosmosDbContextOptionsExtensionsTests.cs +++ b/test/EFCore.Cosmos.Tests/Extensions/CosmosDbContextOptionsExtensionsTests.cs @@ -65,6 +65,7 @@ public void Can_create_options_with_valid_values() Test(o => o.MaxTcpConnectionsPerEndpoint(3), o => Assert.Equal(3, o.MaxTcpConnectionsPerEndpoint)); Test(o => o.LimitToEndpoint(), o => Assert.True(o.LimitToEndpoint)); Test(o => o.ContentResponseOnWriteEnabled(), o => Assert.True(o.EnableContentResponseOnWrite)); + Test(o => o.BulkExecutionEnabled(), o => Assert.True(o.EnableBulkExecution)); var webProxy = new WebProxy(); Test(o => o.WebProxy(webProxy), o => Assert.Same(webProxy, o.WebProxy));