Skip to content

Commit

Permalink
Add MinBatchSize option
Browse files Browse the repository at this point in the history
Part of #9270
  • Loading branch information
AndriySvyryd committed Oct 16, 2017
1 parent 36e12e8 commit bc8e814
Show file tree
Hide file tree
Showing 11 changed files with 237 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ protected RelationalDbContextOptionsBuilder([NotNull] DbContextOptionsBuilder op
public virtual TBuilder MaxBatchSize(int maxBatchSize)
=> WithOption(e => (TExtension)e.WithMaxBatchSize(maxBatchSize));

/// <summary>
/// Configures the minimum number of statements that are needed for a multi-statement command sent to the database
/// during <see cref="DbContext.SaveChanges()" />.
/// </summary>
/// <param name="minBatchSize"> The minimum number of statements. </param>
/// <returns> The same builder instance so that multiple calls can be chained. </returns>
public virtual TBuilder MinBatchSize(int minBatchSize)
=> WithOption(e => (TExtension)e.WithMinBatchSize(minBatchSize));

/// <summary>
/// Configures the wait time (in seconds) before terminating the attempt to execute a command and generating an error.
/// </summary>
Expand Down
29 changes: 29 additions & 0 deletions src/EFCore.Relational/Infrastructure/RelationalOptionsExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public abstract class RelationalOptionsExtension : IDbContextOptionsExtension
private DbConnection _connection;
private int? _commandTimeout;
private int? _maxBatchSize;
private int? _minBatchSize;
private bool _useRelationalNulls;
private string _migrationsAssembly;
private string _migrationsHistoryTableName;
Expand All @@ -58,6 +59,7 @@ protected RelationalOptionsExtension([NotNull] RelationalOptionsExtension copyFr
_connection = copyFrom._connection;
_commandTimeout = copyFrom._commandTimeout;
_maxBatchSize = copyFrom._maxBatchSize;
_minBatchSize = copyFrom._minBatchSize;
_useRelationalNulls = copyFrom._useRelationalNulls;
_migrationsAssembly = copyFrom._migrationsAssembly;
_migrationsHistoryTableName = copyFrom._migrationsHistoryTableName;
Expand Down Expand Up @@ -170,6 +172,33 @@ public virtual RelationalOptionsExtension WithMaxBatchSize(int? maxBatchSize)
return clone;
}

/// <summary>
/// The minimum number of statements that are needed for a multi-statement command sent to the database
/// during <see cref="DbContext.SaveChanges()" /> or <c>null</c> if none has been set.
/// </summary>
public virtual int? MinBatchSize => _minBatchSize;

/// <summary>
/// Creates a new instance with all options the same as for this instance, but with the given option changed.
/// It is unusual to call this method directly. Instead use <see cref="DbContextOptionsBuilder" />.
/// </summary>
/// <param name="minBatchSize"> The option to change. </param>
/// <returns> A new instance with the option changed. </returns>
public virtual RelationalOptionsExtension WithMinBatchSize(int? minBatchSize)
{
if (minBatchSize.HasValue
&& minBatchSize <= 0)
{
throw new InvalidOperationException(RelationalStrings.InvalidMinBatchSize);
}

var clone = Clone();

clone._minBatchSize = minBatchSize;

return clone;
}

/// <summary>
/// Indicates whether or not to use relational database semantics when comparing null values. By default,
/// Entity Framework will use C# semantics for null values, and generate SQL to compensate for differences
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Utilities;

namespace Microsoft.EntityFrameworkCore.Metadata
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/EFCore.Relational/Properties/RelationalStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -440,4 +440,7 @@
<data name="ConflictingEnlistedTransaction" xml:space="preserve">
<value>The connection is currently enlisted in a transaction. The enlisted transaction needs to be completed before starting a transaction.</value>
</data>
<data name="InvalidMinBatchSize" xml:space="preserve">
<value>The specified MinBatchSize value is not valid. It must be a positive number.</value>
</data>
</root>
42 changes: 37 additions & 5 deletions src/EFCore.Relational/Update/Internal/CommandBatchPreparer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public class CommandBatchPreparer : ICommandBatchPreparer
private readonly IParameterNameGeneratorFactory _parameterNameGeneratorFactory;
private readonly IComparer<ModificationCommand> _modificationCommandComparer;
private readonly IKeyValueIndexFactorySource _keyValueIndexFactorySource;
private readonly int _minBatchSize;
private IStateManager _stateManager;
private readonly bool _sensitiveLoggingEnabled;

Expand All @@ -39,6 +40,8 @@ public CommandBatchPreparer([NotNull] CommandBatchPreparerDependencies dependenc
_parameterNameGeneratorFactory = dependencies.ParameterNameGeneratorFactory;
_modificationCommandComparer = dependencies.ModificationCommandComparer;
_keyValueIndexFactorySource = dependencies.KeyValueIndexFactorySource;
_minBatchSize = dependencies.Options.Extensions.OfType<RelationalOptionsExtension>().FirstOrDefault()
?.MinBatchSize ?? 4;
Dependencies = dependencies;

if (dependencies.LoggingOptions.IsSensitiveDataLoggingEnabled)
Expand Down Expand Up @@ -73,17 +76,46 @@ public virtual IEnumerable<ModificationCommandBatch> BatchCommands(IReadOnlyList

if (!batch.AddCommand(modificationCommand))
{
yield return batch;
parameterNameGenerator.Reset();
batch = _modificationCommandBatchFactory.Create();
batch.AddCommand(modificationCommand);
if (batch.ModificationCommands.Count == 1
|| batch.ModificationCommands.Count >= _minBatchSize)
{
yield return batch;
}
else
{
foreach (var command in batch.ModificationCommands)
{
yield return StartNewBatch(parameterNameGenerator, command);
}
}

batch = StartNewBatch(parameterNameGenerator, modificationCommand);
}
}

yield return batch;
if (batch.ModificationCommands.Count == 1
|| batch.ModificationCommands.Count >= _minBatchSize)
{
yield return batch;
}
else
{
foreach (var command in batch.ModificationCommands)
{
yield return StartNewBatch(parameterNameGenerator, command);
}
}
}
}

private ModificationCommandBatch StartNewBatch(ParameterNameGenerator parameterNameGenerator, ModificationCommand modificationCommand)
{
parameterNameGenerator.Reset();
var batch = _modificationCommandBatchFactory.Create();
batch.AddCommand(modificationCommand);
return batch;
}

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;

namespace Microsoft.EntityFrameworkCore.Update.Internal
Expand Down Expand Up @@ -52,14 +53,16 @@ public CommandBatchPreparerDependencies(
[NotNull] IComparer<ModificationCommand> modificationCommandComparer,
[NotNull] IKeyValueIndexFactorySource keyValueIndexFactorySource,
[NotNull] Func<IStateManager> stateManager,
[NotNull] ILoggingOptions loggingOptions)
[NotNull] ILoggingOptions loggingOptions,
[NotNull] IDbContextOptions options)
{
ModificationCommandBatchFactory = modificationCommandBatchFactory;
ParameterNameGeneratorFactory = parameterNameGeneratorFactory;
ModificationCommandComparer = modificationCommandComparer;
KeyValueIndexFactorySource = keyValueIndexFactorySource;
StateManager = stateManager;
LoggingOptions = loggingOptions;
Options = options;
}

/// <summary>
Expand Down Expand Up @@ -98,6 +101,12 @@ public CommandBatchPreparerDependencies(
/// </summary>
public ILoggingOptions LoggingOptions { get; }

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public IDbContextOptions Options { get; }

/// <summary>
/// Clones this dependency parameter object with one service replaced.
/// </summary>
Expand All @@ -110,7 +119,8 @@ public CommandBatchPreparerDependencies With([NotNull] IModificationCommandBatch
ModificationCommandComparer,
KeyValueIndexFactorySource,
StateManager,
LoggingOptions);
LoggingOptions,
Options);

/// <summary>
/// Clones this dependency parameter object with one service replaced.
Expand All @@ -124,7 +134,8 @@ public CommandBatchPreparerDependencies With([NotNull] IParameterNameGeneratorFa
ModificationCommandComparer,
KeyValueIndexFactorySource,
StateManager,
LoggingOptions);
LoggingOptions,
Options);

/// <summary>
/// Clones this dependency parameter object with one service replaced.
Expand All @@ -138,7 +149,8 @@ public CommandBatchPreparerDependencies With([NotNull] IComparer<ModificationCom
modificationCommandComparer,
KeyValueIndexFactorySource,
StateManager,
LoggingOptions);
LoggingOptions,
Options);

/// <summary>
/// Clones this dependency parameter object with one service replaced.
Expand All @@ -152,7 +164,8 @@ public CommandBatchPreparerDependencies With([NotNull] IKeyValueIndexFactorySour
ModificationCommandComparer,
keyValueIndexFactorySource,
StateManager,
LoggingOptions);
LoggingOptions,
Options);

/// <summary>
/// Clones this dependency parameter object with one service replaced.
Expand All @@ -166,7 +179,8 @@ public CommandBatchPreparerDependencies With([NotNull] Func<IStateManager> state
ModificationCommandComparer,
KeyValueIndexFactorySource,
stateManager,
LoggingOptions);
LoggingOptions,
Options);

/// <summary>
/// Clones this dependency parameter object with one service replaced.
Expand All @@ -180,6 +194,22 @@ public CommandBatchPreparerDependencies With([NotNull] ILoggingOptions loggingOp
ModificationCommandComparer,
KeyValueIndexFactorySource,
StateManager,
loggingOptions);
loggingOptions,
Options);

/// <summary>
/// Clones this dependency parameter object with one service replaced.
/// </summary>
/// <param name="options"> A replacement for the current dependency of this type. </param>
/// <returns> A new parameter object with the given service replaced. </returns>
public CommandBatchPreparerDependencies With([NotNull] IDbContextOptions options)
=> new CommandBatchPreparerDependencies(
ModificationCommandBatchFactory,
ParameterNameGeneratorFactory,
ModificationCommandComparer,
KeyValueIndexFactorySource,
StateManager,
LoggingOptions,
options);
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.TestUtilities.FakeProvider;
using Microsoft.EntityFrameworkCore.Update;
using Microsoft.EntityFrameworkCore.Update.Internal;
using Microsoft.Extensions.DependencyInjection;

namespace Microsoft.EntityFrameworkCore.TestUtilities
Expand All @@ -20,33 +15,6 @@ protected RelationalTestHelpers()

public static RelationalTestHelpers Instance { get; } = new RelationalTestHelpers();

public ICommandBatchPreparer CreateCommandBatchPreparer(
IModificationCommandBatchFactory modificationCommandBatchFactory = null,
IStateManager stateManager = null,
bool sensitiveLogging = false)
{
modificationCommandBatchFactory =
modificationCommandBatchFactory
?? Instance.CreateContextServices().GetRequiredService<IModificationCommandBatchFactory>();

stateManager = stateManager
?? Instance.CreateContextServices().GetRequiredService<IStateManager>();

var loggingOptions = new LoggingOptions();
if (sensitiveLogging)
{
loggingOptions.Initialize(new DbContextOptionsBuilder<DbContext>().EnableSensitiveDataLogging().Options);
}

return new CommandBatchPreparer(new CommandBatchPreparerDependencies(
modificationCommandBatchFactory,
new ParameterNameGeneratorFactory(new ParameterNameGeneratorDependencies()),
new ModificationCommandComparer(),
new KeyValueIndexFactorySource(),
() => stateManager,
loggingOptions));
}

public override IServiceCollection AddProviderServices(IServiceCollection services)
=> FakeRelationalOptionsExtension.AddEntityFrameworkRelationalDatabase(services);

Expand Down
Loading

0 comments on commit bc8e814

Please sign in to comment.